From 0e07d43b914323c030fca65c83356e82d59a9fa4 Mon Sep 17 00:00:00 2001 From: I326463 Date: Wed, 23 Jun 2021 16:46:14 +0300 Subject: [PATCH 001/105] Update yaml.v2 to newer version --- go.mod | 2 +- go.sum | 4 +- vendor/gopkg.in/yaml.v2/.travis.yml | 18 ++-- vendor/gopkg.in/yaml.v2/decode.go | 48 ++++++++++- vendor/gopkg.in/yaml.v2/encode.go | 28 +++++++ vendor/gopkg.in/yaml.v2/resolve.go | 2 +- vendor/gopkg.in/yaml.v2/scannerc.go | 123 ++++++++++++++++------------ vendor/gopkg.in/yaml.v2/yaml.go | 2 +- vendor/gopkg.in/yaml.v2/yamlh.go | 1 + vendor/modules.txt | 2 +- 10 files changed, 159 insertions(+), 71 deletions(-) diff --git a/go.mod b/go.mod index 404c5fed..0052e2c8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module sigs.k8s.io/structured-merge-diff/v4 -require gopkg.in/yaml.v2 v2.2.1 +require gopkg.in/yaml.v2 v2.2.8 require ( github.com/google/gofuzz v1.0.0 diff --git a/go.sum b/go.sum index a31489b9..34a613bc 100644 --- a/go.sum +++ b/go.sum @@ -15,5 +15,5 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml index 9f556934..055480b9 100644 --- a/vendor/gopkg.in/yaml.v2/.travis.yml +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -1,12 +1,16 @@ language: go go: - - 1.4 - - 1.5 - - 1.6 - - 1.7 - - 1.8 - - 1.9 - - tip + - "1.4.x" + - "1.5.x" + - "1.6.x" + - "1.7.x" + - "1.8.x" + - "1.9.x" + - "1.10.x" + - "1.11.x" + - "1.12.x" + - "1.13.x" + - "tip" go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go index e4e56e28..129bc2a9 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -229,6 +229,10 @@ type decoder struct { mapType reflect.Type terrors []string strict bool + + decodeCount int + aliasCount int + aliasDepth int } var ( @@ -314,7 +318,43 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm return out, false, false } +const ( + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion + alias_ratio_range_low = 400000 + + // 4,000,000 decode operations is ~5MB of dense object declarations, or + // ~4.5MB of dense object declarations with 10% alias expansion + alias_ratio_range_high = 4000000 + + // alias_ratio_range is the range over which we scale allowed alias ratios + alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) +) + +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= alias_ratio_range_low: + // allow 99% to come from alias expansion for small-to-medium documents + return 0.99 + case decodeCount >= alias_ratio_range_high: + // allow 10% to come from alias expansion for very large documents + return 0.10 + default: + // scale smoothly from 99% down to 10% over the range. + // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. + // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). + return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) + } +} + func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { + failf("document contains excessive aliasing") + } switch n.kind { case documentNode: return d.document(n, out) @@ -353,7 +393,9 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { failf("anchor '%s' value contains itself", n.value) } d.aliases[n] = true + d.aliasDepth++ good = d.unmarshal(n.alias, out) + d.aliasDepth-- delete(d.aliases, n) return good } @@ -746,8 +788,7 @@ func (d *decoder) merge(n *node, out reflect.Value) { case mappingNode: d.unmarshal(n, out) case aliasNode: - an, ok := d.doc.anchors[n.value] - if ok && an.kind != mappingNode { + if n.alias != nil && n.alias.kind != mappingNode { failWantMap() } d.unmarshal(n, out) @@ -756,8 +797,7 @@ func (d *decoder) merge(n *node, out reflect.Value) { for i := len(n.children) - 1; i >= 0; i-- { ni := n.children[i] if ni.kind == aliasNode { - an, ok := d.doc.anchors[ni.value] - if ok && an.kind != mappingNode { + if ni.alias != nil && ni.alias.kind != mappingNode { failWantMap() } } else if ni.kind != mappingNode { diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go index a14435e8..0ee738e1 100644 --- a/vendor/gopkg.in/yaml.v2/encode.go +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -13,6 +13,19 @@ import ( "unicode/utf8" ) +// jsonNumber is the interface of the encoding/json.Number datatype. +// Repeating the interface here avoids a dependency on encoding/json, and also +// supports other libraries like jsoniter, which use a similar datatype with +// the same interface. Detecting this interface is useful when dealing with +// structures containing json.Number, which is a string under the hood. The +// encoder should prefer the use of Int64(), Float64() and string(), in that +// order, when encoding this type. +type jsonNumber interface { + Float64() (float64, error) + Int64() (int64, error) + String() string +} + type encoder struct { emitter yaml_emitter_t event yaml_event_t @@ -89,6 +102,21 @@ func (e *encoder) marshal(tag string, in reflect.Value) { } iface := in.Interface() switch m := iface.(type) { + case jsonNumber: + integer, err := m.Int64() + if err == nil { + // In this case the json.Number is a valid int64 + in = reflect.ValueOf(integer) + break + } + float, err := m.Float64() + if err == nil { + // In this case the json.Number is a valid float64 + in = reflect.ValueOf(float) + break + } + // fallback case - no number could be obtained + in = reflect.ValueOf(m.String()) case time.Time, *time.Time: // Although time.Time implements TextMarshaler, // we don't want to treat it as a string for YAML diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go index 6c151db6..4120e0c9 100644 --- a/vendor/gopkg.in/yaml.v2/resolve.go +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -81,7 +81,7 @@ func resolvableTag(tag string) bool { return false } -var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go index 077fd1dd..0b9bb603 100644 --- a/vendor/gopkg.in/yaml.v2/scannerc.go +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -626,30 +626,17 @@ func trace(args ...interface{}) func() { func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { // While we need more tokens to fetch, do it. for { - // Check if we really need to fetch more tokens. - need_more_tokens := false - - if parser.tokens_head == len(parser.tokens) { - // Queue is empty. - need_more_tokens = true - } else { - // Check if any potential simple key may occupy the head position. - if !yaml_parser_stale_simple_keys(parser) { + if parser.tokens_head != len(parser.tokens) { + // If queue is non-empty, check if any potential simple key may + // occupy the head position. + head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] + if !ok { + break + } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok { return false + } else if !valid { + break } - - for i := range parser.simple_keys { - simple_key := &parser.simple_keys[i] - if simple_key.possible && simple_key.token_number == parser.tokens_parsed { - need_more_tokens = true - break - } - } - } - - // We are finished. - if !need_more_tokens { - break } // Fetch the next token. if !yaml_parser_fetch_next_token(parser) { @@ -678,11 +665,6 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { return false } - // Remove obsolete potential simple keys. - if !yaml_parser_stale_simple_keys(parser) { - return false - } - // Check the indentation level against the current column. if !yaml_parser_unroll_indent(parser, parser.mark.column) { return false @@ -837,29 +819,30 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { "found character that cannot start any token") } -// Check the list of potential simple keys and remove the positions that -// cannot contain simple keys anymore. -func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { - // Check for a potential simple key for each flow level. - for i := range parser.simple_keys { - simple_key := &parser.simple_keys[i] - - // The specification requires that a simple key - // - // - is limited to a single line, - // - is shorter than 1024 characters. - if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { - - // Check if the potential simple key to be removed is required. - if simple_key.required { - return yaml_parser_set_scanner_error(parser, - "while scanning a simple key", simple_key.mark, - "could not find expected ':'") - } - simple_key.possible = false +func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) { + if !simple_key.possible { + return false, true + } + + // The 1.2 specification says: + // + // "If the ? indicator is omitted, parsing needs to see past the + // implicit key to recognize it as such. To limit the amount of + // lookahead required, the “:” indicator must appear at most 1024 + // Unicode characters beyond the start of the key. In addition, the key + // is restricted to a single line." + // + if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index { + // Check if the potential simple key to be removed is required. + if simple_key.required { + return false, yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") } + simple_key.possible = false + return false, true } - return true + return true, true } // Check if a simple key may start at the current position and add it if @@ -879,13 +862,14 @@ func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { possible: true, required: required, token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, } - simple_key.mark = parser.mark if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_keys[len(parser.simple_keys)-1] = simple_key + parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1 } return true } @@ -900,19 +884,33 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { "while scanning a simple key", parser.simple_keys[i].mark, "could not find expected ':'") } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number) } - // Remove the key from the stack. - parser.simple_keys[i].possible = false return true } +// max_flow_level limits the flow_level +const max_flow_level = 10000 + // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. - parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{ + possible: false, + required: false, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + mark: parser.mark, + }) // Increase the flow level. parser.flow_level++ + if parser.flow_level > max_flow_level { + return yaml_parser_set_scanner_error(parser, + "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_flow_level)) + } return true } @@ -920,11 +918,16 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { if parser.flow_level > 0 { parser.flow_level-- - parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] + last := len(parser.simple_keys) - 1 + delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number) + parser.simple_keys = parser.simple_keys[:last] } return true } +// max_indents limits the indents stack size +const max_indents = 10000 + // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. @@ -939,6 +942,11 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column + if len(parser.indents) > max_indents { + return yaml_parser_set_scanner_error(parser, + "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_indents)) + } // Create a token and insert it into the queue. token := yaml_token_t{ @@ -989,6 +997,8 @@ func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { // Initialize the simple key stack. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + parser.simple_keys_by_tok = make(map[int]int) + // A simple key is allowed at the beginning of the stream. parser.simple_key_allowed = true @@ -1270,7 +1280,11 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool { simple_key := &parser.simple_keys[len(parser.simple_keys)-1] // Have we found a simple key? - if simple_key.possible { + if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok { + return false + + } else if valid { + // Create the KEY token and insert it into the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, @@ -1288,6 +1302,7 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool { // Remove the simple key. simple_key.possible = false + delete(parser.simple_keys_by_tok, simple_key.token_number) // A simple key cannot follow another simple key. parser.simple_key_allowed = false diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/gopkg.in/yaml.v2/yaml.go index de85aa4c..89650e29 100644 --- a/vendor/gopkg.in/yaml.v2/yaml.go +++ b/vendor/gopkg.in/yaml.v2/yaml.go @@ -89,7 +89,7 @@ func UnmarshalStrict(in []byte, out interface{}) (err error) { return unmarshal(in, out, true) } -// A Decorder reads and decodes YAML values from an input stream. +// A Decoder reads and decodes YAML values from an input stream. type Decoder struct { strict bool parser *parser diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go index e25cee56..f6a9c8e3 100644 --- a/vendor/gopkg.in/yaml.v2/yamlh.go +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -579,6 +579,7 @@ type yaml_parser_t struct { simple_key_allowed bool // May a simple key occur at the current position? simple_keys []yaml_simple_key_t // The stack of simple keys. + simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number // Parser stuff diff --git a/vendor/modules.txt b/vendor/modules.txt index 96d96682..8043cee0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -6,5 +6,5 @@ github.com/json-iterator/go github.com/modern-go/concurrent # github.com/modern-go/reflect2 v1.0.1 github.com/modern-go/reflect2 -# gopkg.in/yaml.v2 v2.2.1 +# gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 From e54e56c696e65703a6368320645cda16c88bd49c Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Thu, 24 Jun 2021 19:39:34 -0700 Subject: [PATCH 002/105] Skip processing of schemaless fieldsets in ReconcileFieldSetWithSchema --- typed/reconcile_schema.go | 28 ++++-- typed/reconcile_schema_test.go | 169 +++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 7 deletions(-) diff --git a/typed/reconcile_schema.go b/typed/reconcile_schema.go index 27b663e3..2b98b729 100644 --- a/typed/reconcile_schema.go +++ b/typed/reconcile_schema.go @@ -124,13 +124,6 @@ func ReconcileFieldSetWithSchema(fieldset *fieldpath.Set, tv *TypedValue) (*fiel v.schema = tv.schema v.typeRef = tv.typeRef - // We don't reconcile deduced types, which are primarily for use by unstructured CRDs. Deduced - // types do not support atomic or granular tags. Nor does the dynamic schema deduction - // interact well with the reconcile logic. - if v.schema == DeducedParseableType.Schema { - return nil, nil - } - defer v.finished() errs := v.reconcile() @@ -229,6 +222,12 @@ func (v *reconcileWithSchemaWalker) visitMapItems(t *schema.Map, element *fieldp } func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors) { + // We don't currently reconcile deduced types (unstructured CRDs) or maps that contain only unknown + // fields since deduced types do not yet support atomic or granular tags. + if isUntypedDeducedMap(t) { + return errs + } + // reconcile maps and structs changed from granular to atomic. // Note that migrations from atomic to granular are not recommended and will // be treated as if they were always granular. @@ -274,3 +273,18 @@ func typeRefAtPath(t *schema.Map, pe fieldpath.PathElement) (schema.TypeRef, boo } return tr, tr != schema.TypeRef{} } + +// isUntypedDeducedMap returns true if m has no fields defined, but allows untyped elements. +// This is equivalent to a openAPI object that has x-kubernetes-preserve-unknown-fields=true +// but does not have any properties defined on the object. +func isUntypedDeducedMap(m *schema.Map) bool { + return isUntypedDeducedRef(m.ElementType) && m.Fields == nil +} + +func isUntypedDeducedRef(t schema.TypeRef) bool { + if t.NamedType != nil { + return *t.NamedType == "__untyped_deduced_" + } + atom := t.Inlined + return atom.Scalar != nil && *atom.Scalar == "untyped" +} diff --git a/typed/reconcile_schema_test.go b/typed/reconcile_schema_test.go index 03bfd49c..72053af2 100644 --- a/typed/reconcile_schema_test.go +++ b/typed/reconcile_schema_test.go @@ -98,6 +98,66 @@ func granularSchema(version string) typed.YAMLObject { - name: numeric type: scalar: numeric +- name: empty + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: emptyWithPreserveUnknown + map: + fields: + - name: preserveField + type: + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: populatedWithPreserveUnknown + map: + fields: + - name: preserveField + type: + map: + fields: + - name: list + type: + namedType: list + elementType: + namedType: __untyped_deduced_ +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable `, version)) } @@ -164,6 +224,66 @@ func atomicSchema(version string) typed.YAMLObject { - name: numeric type: scalar: numeric +- name: empty + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: emptyWithPreserveUnknown + map: + fields: + - name: preserveField + type: + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: populatedWithPreserveUnknown + map: + fields: + - name: preserveField + type: + map: + fields: + - name: list + type: + namedType: list + elementType: + namedType: __untyped_deduced_ +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable `, version)) } @@ -257,6 +377,55 @@ unchanged: {} _P("unchanged"), ), fixedFields: nil, // indicates no change +}, { + name: "deduced", + rootTypeName: "empty", + oldSchema: atomicSchema("v1"), + newSchema: granularSchema("v1"), + liveObject: basicLiveObject, + oldFields: _NS( + _P("struct"), + _P("list"), + _P("objectList"), + _P("unchanged", "numeric"), + ), + fixedFields: nil, // indicates no change +}, { + name: "empty-preserve-unknown", + rootTypeName: "emptyWithPreserveUnknown", + oldSchema: atomicSchema("v1"), + newSchema: granularSchema("v1"), + liveObject: typed.YAMLObject(` +preserveField: + arbitrary: abc +`), + oldFields: _NS( + _P("preserveField"), + _P("preserveField", "arbitrary"), + ), + fixedFields: nil, // indicates no change +}, { + name: "populated-preserve-unknown", + rootTypeName: "populatedWithPreserveUnknown", + oldSchema: granularSchema("v1"), + newSchema: atomicSchema("v1"), + liveObject: typed.YAMLObject(` +preserveField: + arbitrary: abc + list: + - one +`), + oldFields: _NS( + _P("preserveField"), + _P("preserveField", "arbitrary"), + _P("preserveField", "list"), + _P("preserveField", "list", _V("one")), + ), + fixedFields: _NS( + _P("preserveField"), + _P("preserveField", "arbitrary"), + _P("preserveField", "list"), + ), }} func TestReconcileFieldSetWithSchema(t *testing.T) { From b4c53cbfc9dcc493a7d496d6332aa56284300191 Mon Sep 17 00:00:00 2001 From: Jiahui Feng Date: Wed, 27 Oct 2021 16:59:03 -0700 Subject: [PATCH 003/105] Prefer rhs ordering while merging list. (#203) * add list merge ordering test. * implement new rhs-preferred ordering. - merge and add each child of rhs - append each remaining child of lhs * update ordering in tests to reflect new ordering algorithm. * simplify merging algorithm. * simplify again. * additional tests for ordered merging. --- typed/merge.go | 58 +++++++++++++++++++++------------------------ typed/merge_test.go | 21 ++++++++++++---- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 7e20f408..afec9f3c 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -172,55 +172,51 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, int(math.Max(float64(rLen), float64(lLen)))) - // TODO: ordering is totally wrong. - // TODO: might as well make the map order work the same way. + lhsOrder := make([]fieldpath.PathElement, 0, lLen) - // This is a cheap hack to at least make the output order stable. - rhsOrder := make([]fieldpath.PathElement, 0, rLen) - - // First, collect all RHS children. - observedRHS := fieldpath.MakePathElementValueMap(rLen) - if rhs != nil { - for i := 0; i < rhs.Length(); i++ { - child := rhs.At(i) + // First, collect all LHS children. + observedLHS := fieldpath.MakePathElementValueMap(lLen) + if lhs != nil { + for i := 0; i < lhs.Length(); i++ { + child := lhs.At(i) pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) if err != nil { - errs = append(errs, errorf("rhs: element %v: %v", i, err.Error())...) + errs = append(errs, errorf("lhs: element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't // even report errors deeper in the schema, so bail on // this element. continue } - if _, ok := observedRHS.Get(pe); ok { - errs = append(errs, errorf("rhs: duplicate entries for key %v", pe.String())...) + if _, ok := observedLHS.Get(pe); ok { + errs = append(errs, errorf("lhs: duplicate entries for key %v", pe.String())...) } - observedRHS.Insert(pe, child) - rhsOrder = append(rhsOrder, pe) + observedLHS.Insert(pe, child) + lhsOrder = append(lhsOrder, pe) } } - // Then merge with LHS children. - observedLHS := fieldpath.MakePathElementSet(lLen) - if lhs != nil { - for i := 0; i < lhs.Length(); i++ { - child := lhs.At(i) + // Then merge with RHS children. + observedRHS := fieldpath.MakePathElementSet(rLen) + if rhs != nil { + for i := 0; i < rhs.Length(); i++ { + child := rhs.At(i) pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) if err != nil { - errs = append(errs, errorf("lhs: element %v: %v", i, err.Error())...) + errs = append(errs, errorf("rhs: element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't // even report errors deeper in the schema, so bail on // this element. continue } - if observedLHS.Has(pe) { - errs = append(errs, errorf("lhs: duplicate entries for key %v", pe.String())...) + if observedRHS.Has(pe) { + errs = append(errs, errorf("rhs: duplicate entries for key %v", pe.String())...) continue } - observedLHS.Insert(pe) + observedRHS.Insert(pe) w2 := w.prepareDescent(pe, t.ElementType) - w2.lhs = value.Value(child) - if rchild, ok := observedRHS.Get(pe); ok { - w2.rhs = rchild + w2.rhs = child + if lchild, ok := observedLHS.Get(pe); ok { + w2.lhs = lchild } errs = append(errs, w2.merge(pe.String)...) if w2.out != nil { @@ -230,13 +226,13 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } } - for _, pe := range rhsOrder { - if observedLHS.Has(pe) { + for _, pe := range lhsOrder { + if observedRHS.Has(pe) { continue } - value, _ := observedRHS.Get(pe) + value, _ := observedLHS.Get(pe) w2 := w.prepareDescent(pe, t.ElementType) - w2.rhs = value + w2.lhs = value errs = append(errs, w2.merge(pe.String)...) if w2.out != nil { out = append(out, *w2.out) diff --git a/typed/merge_test.go b/typed/merge_test.go index 5862fb69..bb1e0a38 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -283,12 +283,11 @@ var mergeCases = []mergeTestCase{{ }, { `{"setBool":[true]}`, `{"setBool":[false]}`, - `{"setBool":[true,false]}`, + `{"setBool":[false,true]}`, }, { `{"setNumeric":[1,2,3.14159]}`, `{"setNumeric":[1,2,3]}`, - // KNOWN BUG: this order is wrong - `{"setNumeric":[1,2,3.14159,3]}`, + `{"setNumeric":[1,2,3,3.14159]}`, }}, }, { name: "associative list", @@ -346,11 +345,23 @@ var mergeCases = []mergeTestCase{{ }, { `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`, - `{"list":[{"key":"a","id":1,"value":{"a":"a"}},{"key":"a","id":2,"value":{"a":"a"}}]}`, + `{"list":[{"key":"a","id":2,"value":{"a":"a"}},{"key":"a","id":1,"value":{"a":"a"}}]}`, }, { `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`, `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`, - `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":2}]}`, + `{"list":[{"key":"a","id":1},{"key":"a","id":2},{"key":"b","id":1}]}`, + }, { + `{"list":[{"key":"b","id":2}]}`, + `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, + `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, + }, { + `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, + `{"list":[{"key":"c","id":3},{"key":"b","id":2}]}`, + `{"list":[{"key":"c","id":3},{"key":"b","id":2},{"key":"a","id":1}]}`, + }, { + `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, + `{"list":[{"key":"c","id":3},{"key":"a","id":1}]}`, + `{"list":[{"key":"c","id":3},{"key":"a","id":1},{"key":"b","id":2}]}`, }, { `{"atomicList":["a","a","a"]}`, `{"atomicList":null}`, From 249e3166d5cbccb1d3632a2616124f993db84af1 Mon Sep 17 00:00:00 2001 From: "Jiahui (Jared) Feng" Date: Wed, 1 Dec 2021 13:53:07 -0800 Subject: [PATCH 004/105] new algorithm for structural merge. --- fieldpath/pathelementmap.go | 21 ++++--- fieldpath/pathelementmap_test.go | 16 +++-- typed/merge.go | 101 +++++++++++++++++-------------- typed/merge_test.go | 10 +-- 4 files changed, 84 insertions(+), 64 deletions(-) diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index 9b14ca58..cdc76e70 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -40,6 +40,7 @@ func MakePathElementValueMap(size int) PathElementValueMap { type pathElementValue struct { PathElement PathElement Value value.Value + Index int } type sortedPathElementValues []pathElementValue @@ -48,17 +49,21 @@ type sortedPathElementValues []pathElementValue // be faster than doing it one at a time via Insert. func (spev sortedPathElementValues) Len() int { return len(spev) } func (spev sortedPathElementValues) Less(i, j int) bool { - return spev[i].PathElement.Less(spev[j].PathElement) + cmp := spev[i].PathElement.Compare(spev[j].PathElement) + if cmp != 0 { + return cmp < 0 + } + return spev[i].Index < spev[j].Index } func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], spev[i] } // Insert adds the pathelement and associated value in the map. -func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { +func (s *PathElementValueMap) Insert(pe PathElement, v value.Value, index int) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) if loc == len(s.members) { - s.members = append(s.members, pathElementValue{pe, v}) + s.members = append(s.members, pathElementValue{pe, v, index}) return } if s.members[loc].PathElement.Equals(pe) { @@ -66,20 +71,20 @@ func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { } s.members = append(s.members, pathElementValue{}) copy(s.members[loc+1:], s.members[loc:]) - s.members[loc] = pathElementValue{pe, v} + s.members[loc] = pathElementValue{pe, v, index} } // Get retrieves the value associated with the given PathElement from the map. // (nil, false) is returned if there is no such PathElement. -func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) { +func (s *PathElementValueMap) Get(pe PathElement) (value value.Value, index int, found bool) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) if loc == len(s.members) { - return nil, false + return nil, 0, false } if s.members[loc].PathElement.Equals(pe) { - return s.members[loc].Value, true + return s.members[loc].Value, s.members[loc].Index, true } - return nil, false + return nil, 0, false } diff --git a/fieldpath/pathelementmap_test.go b/fieldpath/pathelementmap_test.go index cbabf50f..3d38ad35 100644 --- a/fieldpath/pathelementmap_test.go +++ b/fieldpath/pathelementmap_test.go @@ -25,26 +25,30 @@ import ( func TestPathElementValueMap(t *testing.T) { m := PathElementValueMap{} - if _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { + if _, _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { t.Fatal("Unexpected path-element found in empty map") } - m.Insert(PathElement{FieldName: strptr("carrot")}, value.NewValueInterface("knife")) - m.Insert(PathElement{FieldName: strptr("chive")}, value.NewValueInterface(2)) + m.Insert(PathElement{FieldName: strptr("carrot")}, value.NewValueInterface("knife"), 1) + m.Insert(PathElement{FieldName: strptr("chive")}, value.NewValueInterface(2), 2) - if _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { + if _, _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { t.Fatal("Unexpected path-element in map") } - if val, ok := m.Get(PathElement{FieldName: strptr("carrot")}); !ok { + if val, index, ok := m.Get(PathElement{FieldName: strptr("carrot")}); !ok { t.Fatal("Missing path-element in map") } else if !value.Equals(val, value.NewValueInterface("knife")) { t.Fatalf("Unexpected value found: %#v", val) + } else if index != 1 { + t.Fatalf("Unexpected index found: %#v", index) } - if val, ok := m.Get(PathElement{FieldName: strptr("chive")}); !ok { + if val, index, ok := m.Get(PathElement{FieldName: strptr("chive")}); !ok { t.Fatal("Missing path-element in map") } else if !value.Equals(val, value.NewValueInterface(2)) { t.Fatalf("Unexpected value found: %#v", val) + } else if index != 2 { + t.Fatalf("Unexpected index found: %#v", index) } } diff --git a/typed/merge.go b/typed/merge.go index afec9f3c..a8e46e0e 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -172,72 +172,83 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, int(math.Max(float64(rLen), float64(lLen)))) - lhsOrder := make([]fieldpath.PathElement, 0, lLen) - - // First, collect all LHS children. - observedLHS := fieldpath.MakePathElementValueMap(lLen) - if lhs != nil { - for i := 0; i < lhs.Length(); i++ { - child := lhs.At(i) + createPathElementsValues := func(name string, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { + var errs ValidationErrors + length := 0 + if list != nil { + length = list.Length() + } + observed := fieldpath.MakePathElementValueMap(length) + pes := make([]fieldpath.PathElement, 0, length) + for i := 0; i < length; i++ { + child := list.At(i) pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) if err != nil { - errs = append(errs, errorf("lhs: element %v: %v", i, err.Error())...) + errs = append(errs, errorf("%s: element %v: %v", name, i, err.Error())...) // If we can't construct the path element, we can't // even report errors deeper in the schema, so bail on // this element. continue } - if _, ok := observedLHS.Get(pe); ok { - errs = append(errs, errorf("lhs: duplicate entries for key %v", pe.String())...) + if _, _, found := observed.Get(pe); found { + errs = append(errs, errorf("%s: duplicate entries for key %v", name, pe.String())...) + continue } - observedLHS.Insert(pe, child) - lhsOrder = append(lhsOrder, pe) + observed.Insert(pe, child, i) + pes = append(pes, pe) } + return pes, observed, errs } - // Then merge with RHS children. - observedRHS := fieldpath.MakePathElementSet(rLen) - if rhs != nil { - for i := 0; i < rhs.Length(); i++ { - child := rhs.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) - if err != nil { - errs = append(errs, errorf("rhs: element %v: %v", i, err.Error())...) - // If we can't construct the path element, we can't - // even report errors deeper in the schema, so bail on - // this element. - continue - } - if observedRHS.Has(pe) { - errs = append(errs, errorf("rhs: duplicate entries for key %v", pe.String())...) - continue - } - observedRHS.Insert(pe) + lhsOrder, observedLHS, lhsErrs := createPathElementsValues("lhs", lhs) + errs = append(errs, lhsErrs...) + rhsOrder, observedRHS, rhsErrs := createPathElementsValues("rhs", rhs) + errs = append(errs, rhsErrs...) + + lLen, rLen = len(lhsOrder), len(rhsOrder) + for lI, rI := 0, 0; lI < lLen || rI < rLen; { + merge := func(pe fieldpath.PathElement, lChild, rChild value.Value) { w2 := w.prepareDescent(pe, t.ElementType) - w2.rhs = child - if lchild, ok := observedLHS.Get(pe); ok { - w2.lhs = lchild - } + w2.lhs = lChild + w2.rhs = rChild errs = append(errs, w2.merge(pe.String)...) if w2.out != nil { out = append(out, *w2.out) } w.finishDescent(w2) } - } - - for _, pe := range lhsOrder { - if observedRHS.Has(pe) { + if lI < lLen && rI < rLen && lhsOrder[lI].Equals(rhsOrder[rI]) { + // merge LHS & RHS items + pe := lhsOrder[lI] + lChild, _, _ := observedLHS.Get(pe) + rChild, _, _ := observedRHS.Get(pe) + merge(pe, lChild, rChild) + lI++ + rI++ continue } - value, _ := observedLHS.Get(pe) - w2 := w.prepareDescent(pe, t.ElementType) - w2.lhs = value - errs = append(errs, w2.merge(pe.String)...) - if w2.out != nil { - out = append(out, *w2.out) + if lI < lLen { + if _, index, ok := observedRHS.Get(lhsOrder[lI]); ok && index < rI { + // Skip the LHS item because it has already appeared + lI++ + continue + } else if !ok { + // Take the LHS item, without a matching RHS item to merge with + pe := lhsOrder[lI] + lChild, _, _ := observedLHS.Get(pe) + merge(pe, lChild, nil) + lI++ + continue + } + } + if rI < rLen { + // Take the RHS item, merge with matching LHS item if possible + pe := rhsOrder[rI] + rChild, _, _ := observedRHS.Get(pe) + lChild, _, _ := observedLHS.Get(pe) // may be nil + merge(pe, lChild, rChild) + rI++ } - w.finishDescent(w2) } if len(out) > 0 { diff --git a/typed/merge_test.go b/typed/merge_test.go index bb1e0a38..f6bd1cf8 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -283,11 +283,11 @@ var mergeCases = []mergeTestCase{{ }, { `{"setBool":[true]}`, `{"setBool":[false]}`, - `{"setBool":[false,true]}`, + `{"setBool":[true, false]}`, }, { `{"setNumeric":[1,2,3.14159]}`, `{"setNumeric":[1,2,3]}`, - `{"setNumeric":[1,2,3,3.14159]}`, + `{"setNumeric":[1,2,3.14159,3]}`, }}, }, { name: "associative list", @@ -345,11 +345,11 @@ var mergeCases = []mergeTestCase{{ }, { `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`, `{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`, - `{"list":[{"key":"a","id":2,"value":{"a":"a"}},{"key":"a","id":1,"value":{"a":"a"}}]}`, + `{"list":[{"key":"a","id":1,"value":{"a":"a"}},{"key":"a","id":2,"value":{"a":"a"}}]}`, }, { `{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`, `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`, - `{"list":[{"key":"a","id":1},{"key":"a","id":2},{"key":"b","id":1}]}`, + `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":2}]}`, }, { `{"list":[{"key":"b","id":2}]}`, `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, @@ -357,7 +357,7 @@ var mergeCases = []mergeTestCase{{ }, { `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, `{"list":[{"key":"c","id":3},{"key":"b","id":2}]}`, - `{"list":[{"key":"c","id":3},{"key":"b","id":2},{"key":"a","id":1}]}`, + `{"list":[{"key":"a","id":1},{"key":"c","id":3},{"key":"b","id":2}]}`, }, { `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, `{"list":[{"key":"c","id":3},{"key":"a","id":1}]}`, From 602f4c3fca569adaa40becac563dad67ab2a98bd Mon Sep 17 00:00:00 2001 From: Indeed Date: Thu, 2 Dec 2021 17:10:00 -0800 Subject: [PATCH 005/105] use a "seen" set instead of index. --- fieldpath/pathelementmap.go | 21 ++++++++------------- fieldpath/pathelementmap_test.go | 16 ++++++---------- typed/merge.go | 30 +++++++++++++++++------------- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index cdc76e70..9b14ca58 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -40,7 +40,6 @@ func MakePathElementValueMap(size int) PathElementValueMap { type pathElementValue struct { PathElement PathElement Value value.Value - Index int } type sortedPathElementValues []pathElementValue @@ -49,21 +48,17 @@ type sortedPathElementValues []pathElementValue // be faster than doing it one at a time via Insert. func (spev sortedPathElementValues) Len() int { return len(spev) } func (spev sortedPathElementValues) Less(i, j int) bool { - cmp := spev[i].PathElement.Compare(spev[j].PathElement) - if cmp != 0 { - return cmp < 0 - } - return spev[i].Index < spev[j].Index + return spev[i].PathElement.Less(spev[j].PathElement) } func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], spev[i] } // Insert adds the pathelement and associated value in the map. -func (s *PathElementValueMap) Insert(pe PathElement, v value.Value, index int) { +func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) if loc == len(s.members) { - s.members = append(s.members, pathElementValue{pe, v, index}) + s.members = append(s.members, pathElementValue{pe, v}) return } if s.members[loc].PathElement.Equals(pe) { @@ -71,20 +66,20 @@ func (s *PathElementValueMap) Insert(pe PathElement, v value.Value, index int) { } s.members = append(s.members, pathElementValue{}) copy(s.members[loc+1:], s.members[loc:]) - s.members[loc] = pathElementValue{pe, v, index} + s.members[loc] = pathElementValue{pe, v} } // Get retrieves the value associated with the given PathElement from the map. // (nil, false) is returned if there is no such PathElement. -func (s *PathElementValueMap) Get(pe PathElement) (value value.Value, index int, found bool) { +func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) if loc == len(s.members) { - return nil, 0, false + return nil, false } if s.members[loc].PathElement.Equals(pe) { - return s.members[loc].Value, s.members[loc].Index, true + return s.members[loc].Value, true } - return nil, 0, false + return nil, false } diff --git a/fieldpath/pathelementmap_test.go b/fieldpath/pathelementmap_test.go index 3d38ad35..cbabf50f 100644 --- a/fieldpath/pathelementmap_test.go +++ b/fieldpath/pathelementmap_test.go @@ -25,30 +25,26 @@ import ( func TestPathElementValueMap(t *testing.T) { m := PathElementValueMap{} - if _, _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { + if _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { t.Fatal("Unexpected path-element found in empty map") } - m.Insert(PathElement{FieldName: strptr("carrot")}, value.NewValueInterface("knife"), 1) - m.Insert(PathElement{FieldName: strptr("chive")}, value.NewValueInterface(2), 2) + m.Insert(PathElement{FieldName: strptr("carrot")}, value.NewValueInterface("knife")) + m.Insert(PathElement{FieldName: strptr("chive")}, value.NewValueInterface(2)) - if _, _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { + if _, ok := m.Get(PathElement{FieldName: strptr("onion")}); ok { t.Fatal("Unexpected path-element in map") } - if val, index, ok := m.Get(PathElement{FieldName: strptr("carrot")}); !ok { + if val, ok := m.Get(PathElement{FieldName: strptr("carrot")}); !ok { t.Fatal("Missing path-element in map") } else if !value.Equals(val, value.NewValueInterface("knife")) { t.Fatalf("Unexpected value found: %#v", val) - } else if index != 1 { - t.Fatalf("Unexpected index found: %#v", index) } - if val, index, ok := m.Get(PathElement{FieldName: strptr("chive")}); !ok { + if val, ok := m.Get(PathElement{FieldName: strptr("chive")}); !ok { t.Fatal("Missing path-element in map") } else if !value.Equals(val, value.NewValueInterface(2)) { t.Fatalf("Unexpected value found: %#v", val) - } else if index != 2 { - t.Fatalf("Unexpected index found: %#v", index) } } diff --git a/typed/merge.go b/typed/merge.go index a8e46e0e..3911a3ad 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -17,8 +17,6 @@ limitations under the License. package typed import ( - "math" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/schema" "sigs.k8s.io/structured-merge-diff/v4/value" @@ -170,7 +168,11 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if lhs != nil { lLen = lhs.Length() } - out := make([]interface{}, 0, int(math.Max(float64(rLen), float64(lLen)))) + outLen := lLen + if outLen < rLen { + outLen = rLen + } + out := make([]interface{}, 0, outLen) createPathElementsValues := func(name string, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { var errs ValidationErrors @@ -190,11 +192,11 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // this element. continue } - if _, _, found := observed.Get(pe); found { + if _, found := observed.Get(pe); found { errs = append(errs, errorf("%s: duplicate entries for key %v", name, pe.String())...) continue } - observed.Insert(pe, child, i) + observed.Insert(pe, child) pes = append(pes, pe) } return pes, observed, errs @@ -204,6 +206,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err errs = append(errs, lhsErrs...) rhsOrder, observedRHS, rhsErrs := createPathElementsValues("rhs", rhs) errs = append(errs, rhsErrs...) + seen := fieldpath.MakePathElementSet(outLen) lLen, rLen = len(lhsOrder), len(rhsOrder) for lI, rI := 0, 0; lI < lLen || rI < rLen; { @@ -216,26 +219,27 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err out = append(out, *w2.out) } w.finishDescent(w2) + seen.Insert(pe) } if lI < lLen && rI < rLen && lhsOrder[lI].Equals(rhsOrder[rI]) { // merge LHS & RHS items pe := lhsOrder[lI] - lChild, _, _ := observedLHS.Get(pe) - rChild, _, _ := observedRHS.Get(pe) + lChild, _ := observedLHS.Get(pe) + rChild, _ := observedRHS.Get(pe) merge(pe, lChild, rChild) lI++ rI++ continue } if lI < lLen { - if _, index, ok := observedRHS.Get(lhsOrder[lI]); ok && index < rI { + pe := lhsOrder[lI] + if ok := seen.Has(pe); ok { // Skip the LHS item because it has already appeared lI++ continue - } else if !ok { + } else if _, ok := observedRHS.Get(pe); !ok { // Take the LHS item, without a matching RHS item to merge with - pe := lhsOrder[lI] - lChild, _, _ := observedLHS.Get(pe) + lChild, _ := observedLHS.Get(pe) merge(pe, lChild, nil) lI++ continue @@ -244,8 +248,8 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if rI < rLen { // Take the RHS item, merge with matching LHS item if possible pe := rhsOrder[rI] - rChild, _, _ := observedRHS.Get(pe) - lChild, _, _ := observedLHS.Get(pe) // may be nil + rChild, _ := observedRHS.Get(pe) + lChild, _ := observedLHS.Get(pe) // may be nil merge(pe, lChild, rChild) rI++ } From 354bb100581bf0756a8d6a09640fdec7db41b059 Mon Sep 17 00:00:00 2001 From: Indeed Date: Thu, 2 Dec 2021 17:28:57 -0800 Subject: [PATCH 006/105] extract help functions. --- typed/merge.go | 104 ++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 3911a3ad..3f063fac 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -174,59 +174,25 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, outLen) - createPathElementsValues := func(name string, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { - var errs ValidationErrors - length := 0 - if list != nil { - length = list.Length() - } - observed := fieldpath.MakePathElementValueMap(length) - pes := make([]fieldpath.PathElement, 0, length) - for i := 0; i < length; i++ { - child := list.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) - if err != nil { - errs = append(errs, errorf("%s: element %v: %v", name, i, err.Error())...) - // If we can't construct the path element, we can't - // even report errors deeper in the schema, so bail on - // this element. - continue - } - if _, found := observed.Get(pe); found { - errs = append(errs, errorf("%s: duplicate entries for key %v", name, pe.String())...) - continue - } - observed.Insert(pe, child) - pes = append(pes, pe) - } - return pes, observed, errs - } - - lhsOrder, observedLHS, lhsErrs := createPathElementsValues("lhs", lhs) + lhsOrder, observedLHS, lhsErrs := w.visitListOperand(t, lhs) errs = append(errs, lhsErrs...) - rhsOrder, observedRHS, rhsErrs := createPathElementsValues("rhs", rhs) + rhsOrder, observedRHS, rhsErrs := w.visitListOperand(t, rhs) errs = append(errs, rhsErrs...) seen := fieldpath.MakePathElementSet(outLen) lLen, rLen = len(lhsOrder), len(rhsOrder) for lI, rI := 0, 0; lI < lLen || rI < rLen; { - merge := func(pe fieldpath.PathElement, lChild, rChild value.Value) { - w2 := w.prepareDescent(pe, t.ElementType) - w2.lhs = lChild - w2.rhs = rChild - errs = append(errs, w2.merge(pe.String)...) - if w2.out != nil { - out = append(out, *w2.out) - } - w.finishDescent(w2) - seen.Insert(pe) - } if lI < lLen && rI < rLen && lhsOrder[lI].Equals(rhsOrder[rI]) { // merge LHS & RHS items pe := lhsOrder[lI] lChild, _ := observedLHS.Get(pe) rChild, _ := observedRHS.Get(pe) - merge(pe, lChild, rChild) + mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + seen.Insert(pe) lI++ rI++ continue @@ -240,7 +206,12 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } else if _, ok := observedRHS.Get(pe); !ok { // Take the LHS item, without a matching RHS item to merge with lChild, _ := observedLHS.Get(pe) - merge(pe, lChild, nil) + mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + seen.Insert(pe) lI++ continue } @@ -250,7 +221,12 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err pe := rhsOrder[rI] rChild, _ := observedRHS.Get(pe) lChild, _ := observedLHS.Get(pe) // may be nil - merge(pe, lChild, rChild) + mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + seen.Insert(pe) rI++ } } @@ -263,6 +239,46 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err return errs } +func (w *mergingWalker) visitListOperand(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { + var errs ValidationErrors + length := 0 + if list != nil { + length = list.Length() + } + observed := fieldpath.MakePathElementValueMap(length) + pes := make([]fieldpath.PathElement, 0, length) + for i := 0; i < length; i++ { + child := list.At(i) + pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + if err != nil { + errs = append(errs, errorf("element %v: %v", i, err.Error())...) + // If we can't construct the path element, we can't + // even report errors deeper in the schema, so bail on + // this element. + continue + } + if _, found := observed.Get(pe); found { + errs = append(errs, errorf("duplicate entries for key %v", pe.String())...) + continue + } + observed.Insert(pe, child) + pes = append(pes, pe) + } + return pes, observed, errs +} + +func (w *mergingWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) (out *interface{}, errs ValidationErrors) { + w2 := w.prepareDescent(pe, t.ElementType) + w2.lhs = lChild + w2.rhs = rChild + errs = append(errs, w2.merge(pe.String)...) + if w2.out != nil { + out = w2.out + } + w.finishDescent(w2) + return +} + func (w *mergingWalker) derefList(prefix string, v value.Value) (value.List, ValidationErrors) { if v == nil { return nil, nil From 063c06f97837edab231cc94e5e0772fe0625e0e8 Mon Sep 17 00:00:00 2001 From: Indeed Date: Thu, 2 Dec 2021 17:55:12 -0800 Subject: [PATCH 007/105] remove unnessary else --- typed/merge.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/typed/merge.go b/typed/merge.go index 3f063fac..749fd96e 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -203,7 +203,8 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // Skip the LHS item because it has already appeared lI++ continue - } else if _, ok := observedRHS.Get(pe); !ok { + } + if _, ok := observedRHS.Get(pe); !ok { // Take the LHS item, without a matching RHS item to merge with lChild, _ := observedLHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) From 2441e09d0c9a054d9e6be3af3dcf7f087846e438 Mon Sep 17 00:00:00 2001 From: Indeed Date: Thu, 2 Dec 2021 17:57:40 -0800 Subject: [PATCH 008/105] visitListOperand -> indexListPathElements --- typed/merge.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 749fd96e..5aff868e 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -174,9 +174,9 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, outLen) - lhsOrder, observedLHS, lhsErrs := w.visitListOperand(t, lhs) + lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) errs = append(errs, lhsErrs...) - rhsOrder, observedRHS, rhsErrs := w.visitListOperand(t, rhs) + rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) errs = append(errs, rhsErrs...) seen := fieldpath.MakePathElementSet(outLen) @@ -240,7 +240,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err return errs } -func (w *mergingWalker) visitListOperand(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { +func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { var errs ValidationErrors length := 0 if list != nil { From 19f740457cdc857176c88977babc2317f6830369 Mon Sep 17 00:00:00 2001 From: Jiahui Feng Date: Fri, 3 Dec 2021 14:56:42 -0800 Subject: [PATCH 009/105] new merge algorithm. --- typed/merge.go | 74 ++++++++++++++++++++++++++++++--------------- typed/merge_test.go | 53 ++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 26 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 5aff868e..75244ef6 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -174,45 +174,64 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, outLen) - lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) - errs = append(errs, lhsErrs...) rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) errs = append(errs, rhsErrs...) - seen := fieldpath.MakePathElementSet(outLen) + lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) + errs = append(errs, lhsErrs...) + + sharedOrder := make([]*fieldpath.PathElement, 0, rLen) + for i := range rhsOrder { + pe := &rhsOrder[i] + if _, ok := observedLHS.Get(*pe); ok { + sharedOrder = append(sharedOrder, pe) + } + } + + var nextShared *fieldpath.PathElement + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } lLen, rLen = len(lhsOrder), len(rhsOrder) for lI, rI := 0, 0; lI < lLen || rI < rLen; { - if lI < lLen && rI < rLen && lhsOrder[lI].Equals(rhsOrder[rI]) { - // merge LHS & RHS items + if lI < lLen && rI < rLen { pe := lhsOrder[lI] - lChild, _ := observedLHS.Get(pe) - rChild, _ := observedRHS.Get(pe) - mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) - errs = append(errs, errs...) - if mergeOut != nil { - out = append(out, *mergeOut) + if pe.Equals(rhsOrder[rI]) { + // merge LHS & RHS items + lChild, _ := observedLHS.Get(pe) + rChild, _ := observedRHS.Get(pe) + mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + lI++ + rI++ + + nextShared = nil + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } + continue } - seen.Insert(pe) - lI++ - rI++ - continue - } - if lI < lLen { - pe := lhsOrder[lI] - if ok := seen.Has(pe); ok { - // Skip the LHS item because it has already appeared + if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsOrder[lI]) { + // shared item, but not the one we want in this round lI++ continue } + } + if lI < lLen { + pe := lhsOrder[lI] if _, ok := observedRHS.Get(pe); !ok { - // Take the LHS item, without a matching RHS item to merge with + // take LHS item lChild, _ := observedLHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) errs = append(errs, errs...) if mergeOut != nil { out = append(out, *mergeOut) } - seen.Insert(pe) lI++ continue } @@ -220,15 +239,22 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if rI < rLen { // Take the RHS item, merge with matching LHS item if possible pe := rhsOrder[rI] - rChild, _ := observedRHS.Get(pe) lChild, _ := observedLHS.Get(pe) // may be nil + rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) errs = append(errs, errs...) if mergeOut != nil { out = append(out, *mergeOut) } - seen.Insert(pe) rI++ + // Advance nextShared, if we are merging nextShared. + if nextShared != nil && nextShared.Equals(pe) { + nextShared = nil + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } + } } } diff --git a/typed/merge_test.go b/typed/merge_test.go index f6bd1cf8..63745cdb 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -280,6 +280,54 @@ var mergeCases = []mergeTestCase{{ `{"setStr":[]}`, `{"setStr":["a","b","c"]}`, `{"setStr":["a","b","c"]}`, + }, { + `{"setStr":["a","b"]}`, + `{"setStr":["b","a"]}`, + `{"setStr":["b","a"]}`, + }, { + `{"setStr":["a","b","c"]}`, + `{"setStr":["d","e","f"]}`, + `{"setStr":["a","b","c","d","e","f"]}`, + }, { + `{"setStr":["a","b","c"]}`, + `{"setStr":["c","d","e","f"]}`, + `{"setStr":["a","b","c","d","e","f"]}`, + }, { + `{"setStr":["a","b","c","g","f"]}`, + `{"setStr":["c","d","e","f"]}`, + `{"setStr":["a","b","c","g","d","e","f"]}`, + }, { + `{"setStr":["a","b","c"]}`, + `{"setStr":["d","e","f","x","y","z"]}`, + `{"setStr":["a","b","c","d","e","f","x","y","z"]}`, + }, { + `{"setStr":["c","d","e","f"]}`, + `{"setStr":["a","c","e"]}`, + `{"setStr":["a","c","d","e","f"]}`, + }, { + `{"setStr":["a","b","c","x","y","z"]}`, + `{"setStr":["d","e","f"]}`, + `{"setStr":["a","b","c","x","y","z","d","e","f"]}`, + }, { + `{"setStr":["a","b","c","x","y","z"]}`, + `{"setStr":["d","e","f","x","y","z"]}`, + `{"setStr":["a","b","c","d","e","f","x","y","z"]}`, + }, { + `{"setStr":["c","a","g","f"]}`, + `{"setStr":["c","f","a","g"]}`, + `{"setStr":["c","f","a","g"]}`, + }, { + `{"setStr":["a","b","c","d"]}`, + `{"setStr":["d","e","f","a"]}`, + `{"setStr":["b","c","d","e","f","a"]}`, + }, { + `{"setStr":["c","d","e","f","g","h","i","j"]}`, + `{"setStr":["2","h","3","e","4","k","l"]}`, + `{"setStr":["c","d","f","g","2","h","i","j","3","e","4","k","l"]}`, + }, { + `{"setStr":["a","b","c","d","e","f","g","h","i","j"]}`, + `{"setStr":["1","b","2","h","3","e","4","k","l"]}`, + `{"setStr":["a","1","b","c","d","f","g","2","h","i","j","3","e","4","k","l"]}`, }, { `{"setBool":[true]}`, `{"setBool":[false]}`, @@ -288,7 +336,8 @@ var mergeCases = []mergeTestCase{{ `{"setNumeric":[1,2,3.14159]}`, `{"setNumeric":[1,2,3]}`, `{"setNumeric":[1,2,3.14159,3]}`, - }}, + }, + }, }, { name: "associative list", rootTypeName: "myRoot", @@ -361,7 +410,7 @@ var mergeCases = []mergeTestCase{{ }, { `{"list":[{"key":"a","id":1},{"key":"b","id":2},{"key":"c","id":3}]}`, `{"list":[{"key":"c","id":3},{"key":"a","id":1}]}`, - `{"list":[{"key":"c","id":3},{"key":"a","id":1},{"key":"b","id":2}]}`, + `{"list":[{"key":"b","id":2},{"key":"c","id":3},{"key":"a","id":1}]}`, }, { `{"atomicList":["a","a","a"]}`, `{"atomicList":null}`, From d2c4b55acb7310639fec96404a753f551d936cce Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Sat, 4 Dec 2021 09:53:27 -0800 Subject: [PATCH 010/105] Add new ApplyTwice operation benchmark --- merge/real_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/merge/real_test.go b/merge/real_test.go index 1136bbdc..29745496 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -100,6 +100,21 @@ func BenchmarkOperations(b *testing.B) { }, }, }, + { + name: "ApplyTwice", + ops: []Operation{ + Apply{ + Manager: "controller", + APIVersion: "v1", + Object: bench.obj, + }, + Apply{ + Manager: "other-controller", + APIVersion: "v1", + Object: bench.obj, + }, + }, + }, { name: "Update", ops: []Operation{ From f0fadd0fca0d34acd6b4483cf42733dd7a789cf7 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 30 Mar 2022 08:34:21 -0700 Subject: [PATCH 011/105] Add README to explain how schemas can be generated --- internal/testdata/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 internal/testdata/README.md diff --git a/internal/testdata/README.md b/internal/testdata/README.md new file mode 100644 index 00000000..17a1a0b0 --- /dev/null +++ b/internal/testdata/README.md @@ -0,0 +1,14 @@ +Generating schemas +================== + +This folder contains a few schemas, which are generally generated by +using `k8s.io/kube-openapi/cmd/openapi2smd`. + +For example, to generate the `k8s-schema.yaml` file, the following +command can be used: + +``` +$ go run k8s.io/kube-openapi/cmd/openapi2smd@latest < \ + <(curl --silent https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json) \ + >k8s-schema.yaml +``` From b2bdf68cd3032837a873fe9e56ca3a3cebbe0171 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 30 Mar 2022 09:11:06 -0700 Subject: [PATCH 012/105] Update jsoniter to v1.1.12 to work with go1.18 --- go.mod | 4 +--- go.sum | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 0052e2c8..0f5fe4b5 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,8 @@ require gopkg.in/yaml.v2 v2.2.8 require ( github.com/google/gofuzz v1.0.0 - github.com/json-iterator/go v1.1.6 + github.com/json-iterator/go v1.1.12 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/stretchr/testify v1.3.0 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 34a613bc..bf04c8ea 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,15 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From c26273c2c44610753dc81bbe0da74752bf9f7f43 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski Date: Thu, 31 Mar 2022 16:15:30 -0700 Subject: [PATCH 013/105] add CopyInto method to schema.Schema --- schema/elements.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/schema/elements.go b/schema/elements.go index 01103b38..45993aba 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -259,3 +259,25 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { } return tr.Inlined, true } + +// Clones this instance of Schema into the other +// If other is nil this method does nothing. +// If other is already initialized, overwrites it with this instance +// Warning: Not thread safe +func (s Schema) CopyInto(dst *Schema) { + if dst == nil { + return + } + + // Schema type is considered immutable so sharing references + dst.Types = s.Types + + if s.m != nil { + // If cache is non-nil then the once token had been consumed. + // Must reset token and use it again to ensure same semantics. + dst.once = sync.Once{} + dst.once.Do(func() { + dst.m = s.m + }) + } +} From f3456370f963a8fff8045d18b0938577aa982190 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Tue, 19 Apr 2022 11:09:33 -0700 Subject: [PATCH 014/105] fixup! add CopyInto method to schema.Schema --- schema/elements_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/schema/elements_test.go b/schema/elements_test.go index c4eca6ef..ae4e383a 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -120,3 +120,44 @@ func TestResolve(t *testing.T) { }) } } + +func TestCopyInto(t *testing.T) { + existing := "existing" + notExisting := "not-existing" + a := Atom{List: &List{}} + + tests := []struct { + testName string + schemaTypeDefs []TypeDef + typeRef TypeRef + }{ + {"noNamedType", nil, TypeRef{Inlined: a}}, + {"notExistingNamedType", nil, TypeRef{NamedType: ¬Existing}}, + {"existingNamedType", []TypeDef{{Name: existing, Atom: a}}, TypeRef{NamedType: &existing}}, + } + + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + t.Parallel() + s := Schema{ + Types: tt.schemaTypeDefs, + } + + theCopy := Schema{} + s.CopyInto(&theCopy) + + if !reflect.DeepEqual(s, theCopy) { + t.Fatal("") + } + + // test after resolve + _, _ = s.Resolve(tt.typeRef) + theCopy = Schema{} + s.CopyInto(&theCopy) + + if !reflect.DeepEqual(s, theCopy) { + t.Fatal("") + } + }) + } +} From 5e01923ce268c7b74a5d578b166261a0b9fa2ccd Mon Sep 17 00:00:00 2001 From: Zach Zhu Date: Sat, 4 Jun 2022 15:12:28 +0800 Subject: [PATCH 015/105] add back owned items at pruned version first this is to avoid conversion failure caused by pruned fields which are required for conversion --- merge/update.go | 61 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/merge/update.go b/merge/update.go index 33a3085b..1b23dcbd 100644 --- a/merge/update.go +++ b/merge/update.go @@ -250,33 +250,54 @@ func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFie } managedAtVersion[managerSet.APIVersion()] = managedAtVersion[managerSet.APIVersion()].Union(managerSet.Set()) } - for version, managed := range managedAtVersion { - merged, err = s.Converter.Convert(merged, version) + // Add back owned items at pruned version first to avoid conversion failure + // caused by pruned fields which are required for conversion. + prunedVersion := fieldpath.APIVersion(*pruned.TypeRef().NamedType) + if managed, ok := managedAtVersion[prunedVersion]; ok { + merged, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, prunedVersion, managed) if err != nil { - if s.Converter.IsMissingVersionError(err) { - continue - } - return nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err) + return nil, err } - pruned, err = s.Converter.Convert(pruned, version) + delete(managedAtVersion, prunedVersion) + } + for version, managed := range managedAtVersion { + merged, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, version, managed) if err != nil { - if s.Converter.IsMissingVersionError(err) { - continue - } - return nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err) + return nil, err } - mergedSet, err := merged.ToFieldSet() - if err != nil { - return nil, fmt.Errorf("failed to create field set from merged object at version %v: %v", version, err) + } + return pruned, nil +} + +// addBackOwnedItemsForVersion adds back any fields, list and map items that were removed by prune with specific managed field path at a version. +// It is an extracted sub-function from addBackOwnedItems for code reuse. +func (s *Updater) addBackOwnedItemsForVersion(merged, pruned *typed.TypedValue, version fieldpath.APIVersion, managed *fieldpath.Set) (*typed.TypedValue, *typed.TypedValue, error) { + var err error + merged, err = s.Converter.Convert(merged, version) + if err != nil { + if s.Converter.IsMissingVersionError(err) { + return merged, pruned, nil } - prunedSet, err := pruned.ToFieldSet() - if err != nil { - return nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err) + return nil, nil, fmt.Errorf("failed to convert merged object at version %v: %v", version, err) + } + pruned, err = s.Converter.Convert(pruned, version) + if err != nil { + if s.Converter.IsMissingVersionError(err) { + return merged, pruned, nil } - sc, tr := merged.Schema(), merged.TypeRef() - pruned = merged.RemoveItems(mergedSet.EnsureNamedFieldsAreMembers(sc, tr).Difference(prunedSet.EnsureNamedFieldsAreMembers(sc, tr).Union(managed.EnsureNamedFieldsAreMembers(sc, tr)))) + return nil, nil, fmt.Errorf("failed to convert pruned object at version %v: %v", version, err) } - return pruned, nil + mergedSet, err := merged.ToFieldSet() + if err != nil { + return nil, nil, fmt.Errorf("failed to create field set from merged object at version %v: %v", version, err) + } + prunedSet, err := pruned.ToFieldSet() + if err != nil { + return nil, nil, fmt.Errorf("failed to create field set from pruned object at version %v: %v", version, err) + } + sc, tr := merged.Schema(), merged.TypeRef() + pruned = merged.RemoveItems(mergedSet.EnsureNamedFieldsAreMembers(sc, tr).Difference(prunedSet.EnsureNamedFieldsAreMembers(sc, tr).Union(managed.EnsureNamedFieldsAreMembers(sc, tr)))) + return merged, pruned, nil } // addBackDanglingItems makes sure that the fields list and map items removed by prune were From 41d6b2c9f2ead827a081d8d049c3c42bfbaad457 Mon Sep 17 00:00:00 2001 From: Zach Zhu Date: Sat, 4 Jun 2022 15:13:10 +0800 Subject: [PATCH 016/105] add test for multiple version appliers with reliant fields conversions --- merge/multiple_appliers_test.go | 131 ++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index 3c47b204..9ebcc662 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -1814,3 +1814,134 @@ func BenchmarkMultipleApplierRecursiveRealConversion(b *testing.B) { } } } + +var multiversionWithReliantFieldsParser = func() Parser { + parser, err := typed.NewParser(`types: +- name: v1 + map: + fields: + - name: field_foo_rely_on_bar + type: + scalar: string + - name: common_field + type: + scalar: string +- name: v2 + map: + fields: + - name: required_field_bar + type: + scalar: string + - name: common_field + type: + scalar: string +`) + if err != nil { + panic(err) + } + return parser +}() + +// reliantFieldsConverter converts v2 obj to v1 relying on the required_field_bar, +// if required_field_bar is empty, the conversion shall fail. +// This converter can only be used with multiversionWithReliantFieldsParser. +type reliantFieldsConverter struct { +} + +var _ merge.Converter = reliantFieldsConverter{} + +func (r reliantFieldsConverter) Convert(v *typed.TypedValue, version fieldpath.APIVersion) (*typed.TypedValue, error) { + inVersion := fieldpath.APIVersion(*v.TypeRef().NamedType) + if inVersion == version { + return v, nil + } + y, err := yaml.Marshal(v.AsValue().Unstructured()) + if err != nil { + return nil, err + } + inStr := string(y) + var outStr string + switch version { + case "v1": + if !strings.Contains(inStr, "required_field_bar") { + return v, fmt.Errorf("missing requried field bar") + } + outStr = strings.Replace(inStr, "required_field_bar", "field_foo_rely_on_bar", -1) + case "v2": + outStr = strings.Replace(inStr, "field_foo_rely_on_bar", "required_field_bar", -1) + default: + return nil, missingVersionError + } + return multiversionWithReliantFieldsParser.Type(string(version)).FromYAML(typed.YAMLObject(outStr)) +} + +func (r reliantFieldsConverter) IsMissingVersionError(err error) bool { + return err == missingVersionError +} + +func TestMultipleAppliersReliantFieldsConversions(t *testing.T) { + tests := map[string]TestCase{ + "multiple_versions_with_reliant_fields": { + Ops: []Operation{ + Apply{ + Manager: "v2_applier", + APIVersion: "v2", + Object: typed.YAMLObject(` + required_field_bar: a + `), + }, + Apply{ + Manager: "v1_applier", + APIVersion: "v1", + Object: typed.YAMLObject(` + common_field: b + `), + }, + Apply{ + Manager: "v2_applier", + APIVersion: "v2", + Object: typed.YAMLObject(` + required_field_bar: b + `), + }, + }, + Object: typed.YAMLObject(` + required_field_bar: b + common_field: b + `), + APIVersion: "v2", + Managed: fieldpath.ManagedFields{ + "v2_applier": fieldpath.NewVersionedSet( + _NS( + _P("required_field_bar"), + ), + "v2", + true, + ), + "v1_applier": fieldpath.NewVersionedSet( + _NS( + _P("common_field"), + ), + "v1", + true, + ), + }, + }, + } + + converter := reliantFieldsConverter{} + for name, test := range tests { + t.Run(name, func(t *testing.T) { + runTimes := 1 + if name == "multiple_versions_with_reliant_fields" { + // run this test for enough times to get as consistent results as possible + runTimes = 100 + } + for i := 0; i < runTimes; i++ { + if err := test.TestWithConverter(multiversionWithReliantFieldsParser, converter); err != nil { + t.Fatal(err) + } + } + }) + } +} From 8a2620fe14f4d77d30b861990c122e56d816b5e4 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 25 Jul 2022 10:37:43 -0700 Subject: [PATCH 017/105] Name operation tests after their filename --- merge/real_test.go | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/merge/real_test.go b/merge/real_test.go index 29745496..e0b80170 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -19,7 +19,6 @@ package merge_test import ( "io/ioutil" "path/filepath" - "strings" "testing" . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" @@ -38,10 +37,6 @@ func read(file string) []byte { return s } -func lastPart(s string) string { - return s[strings.LastIndex(s, ".")+1:] -} - var parser = func() Parser { s := read(testdata("k8s-schema.yaml")) parser, err := typed.NewParser(typed.YAMLObject(s)) @@ -54,28 +49,29 @@ var parser = func() Parser { func BenchmarkOperations(b *testing.B) { benches := []struct { typename string - obj typed.YAMLObject + filename string }{ { typename: "io.k8s.api.core.v1.Pod", - obj: typed.YAMLObject(read(testdata("pod.yaml"))), + filename: "pod.yaml", }, { typename: "io.k8s.api.core.v1.Node", - obj: typed.YAMLObject(read(testdata("node.yaml"))), + filename: "node.yaml", }, { typename: "io.k8s.api.core.v1.Endpoints", - obj: typed.YAMLObject(read(testdata("endpoints.yaml"))), + filename: "endpoints.yaml", }, { typename: "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition", - obj: typed.YAMLObject(read(testdata("prometheus-crd.yaml"))), + filename: "prometheus-crd.yaml", }, } for _, bench := range benches { - b.Run(lastPart(bench.typename), func(b *testing.B) { + b.Run(bench.filename, func(b *testing.B) { + obj := typed.YAMLObject(read(testdata(bench.filename))) tests := []struct { name string ops []Operation @@ -86,7 +82,7 @@ func BenchmarkOperations(b *testing.B) { Update{ Manager: "controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, }, }, @@ -96,7 +92,7 @@ func BenchmarkOperations(b *testing.B) { Apply{ Manager: "controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, }, }, @@ -106,12 +102,12 @@ func BenchmarkOperations(b *testing.B) { Apply{ Manager: "controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, Apply{ Manager: "other-controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, }, }, @@ -121,12 +117,12 @@ func BenchmarkOperations(b *testing.B) { Update{ Manager: "controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, Update{ Manager: "other-controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, }, }, @@ -136,12 +132,12 @@ func BenchmarkOperations(b *testing.B) { Update{ Manager: "controller", APIVersion: "v1", - Object: bench.obj, + Object: obj, }, Update{ Manager: "other-controller", APIVersion: "v2", - Object: bench.obj, + Object: obj, }, }, }, From 11d9bfa9d8d4a9397b1895a88639c51ecd7b090e Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 25 Jul 2022 10:45:22 -0700 Subject: [PATCH 018/105] Change operation test dependency on parseableType rather than typename --- merge/real_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/merge/real_test.go b/merge/real_test.go index e0b80170..d994c789 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -37,7 +37,7 @@ func read(file string) []byte { return s } -var parser = func() Parser { +var k8s = func() Parser { s := read(testdata("k8s-schema.yaml")) parser, err := typed.NewParser(typed.YAMLObject(s)) if err != nil { @@ -48,24 +48,24 @@ var parser = func() Parser { func BenchmarkOperations(b *testing.B) { benches := []struct { - typename string - filename string + parseType typed.ParseableType + filename string }{ { - typename: "io.k8s.api.core.v1.Pod", - filename: "pod.yaml", + parseType: k8s.Type("io.k8s.api.core.v1.Pod"), + filename: "pod.yaml", }, { - typename: "io.k8s.api.core.v1.Node", - filename: "node.yaml", + parseType: k8s.Type("io.k8s.api.core.v1.Node"), + filename: "node.yaml", }, { - typename: "io.k8s.api.core.v1.Endpoints", - filename: "endpoints.yaml", + parseType: k8s.Type("io.k8s.api.core.v1.Endpoints"), + filename: "endpoints.yaml", }, { - typename: "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition", - filename: "prometheus-crd.yaml", + parseType: k8s.Type("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition"), + filename: "prometheus-crd.yaml", }, } @@ -147,7 +147,7 @@ func BenchmarkOperations(b *testing.B) { tc := TestCase{ Ops: test.ops, } - p := SameVersionParser{T: parser.Type(bench.typename)} + p := SameVersionParser{T: bench.parseType} tc.PreprocessOperations(p) b.ReportAllocs() From 0f66c33989965ac7a797c66babec403d9ea8acf8 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 25 Jul 2022 10:56:13 -0700 Subject: [PATCH 019/105] Add exponential test failure for large CR --- internal/testdata/apiresourceimport-cr.yaml | 5992 +++++++++++++++++++ internal/testdata/apiresourceimport.yaml | 280 + merge/real_test.go | 13 + 3 files changed, 6285 insertions(+) create mode 100644 internal/testdata/apiresourceimport-cr.yaml create mode 100644 internal/testdata/apiresourceimport.yaml diff --git a/internal/testdata/apiresourceimport-cr.yaml b/internal/testdata/apiresourceimport-cr.yaml new file mode 100644 index 00000000..dcf89818 --- /dev/null +++ b/internal/testdata/apiresourceimport-cr.yaml @@ -0,0 +1,5992 @@ +apiVersion: apiresource.kcp.dev/v1alpha1 +kind: APIResourceImport +metadata: + annotations: + apiresource.kcp.dev/apiVersion: apps/v1 + clusterName: admin + creationTimestamp: null + name: deployments.us-east1.v1.apps + ownerReferences: + - apiVersion: apiresource.kcp.dev/v1alpha1 + controller: true + kind: Cluster + name: us-east1 + uid: e49223ec-5618-43f5-ae03-ca07c82dd2f3 +spec: + groupVersion: + group: apps + version: v1 + kind: Deployment + listKind: DeploymentList + location: us-east1 + openAPIV3Schema: + description: Deployment enables declarative updates for Pods and ReplicaSets. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest internal + value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object + represents. Servers may infer this from the endpoint the client submits + requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Specification of the desired behavior of the Deployment. + properties: + minReadySeconds: + description: Minimum number of seconds for which a newly created pod should + be ready without any of its container crashing, for it to be considered + available. Defaults to 0 (pod will be considered available as soon as + it is ready) + format: int32 + type: integer + paused: + description: Indicates that the deployment is paused. + type: boolean + progressDeadlineSeconds: + description: The maximum time in seconds for a deployment to make progress + before it is considered to be failed. The deployment controller will + continue to process failed deployments and a condition with a ProgressDeadlineExceeded + reason will be surfaced in the deployment status. Note that progress + will not be estimated during the time a deployment is paused. Defaults + to 600s. + format: int32 + type: integer + replicas: + description: Number of desired pods. This is a pointer to distinguish + between explicit zero and not specified. Defaults to 1. + format: int32 + type: integer + revisionHistoryLimit: + description: The number of old ReplicaSets to retain to allow rollback. + This is a pointer to distinguish between explicit zero and not specified. + Defaults to 10. + format: int32 + type: integer + selector: + description: Label selector for pods. Existing ReplicaSets whose pods + are selected by this will be the ones affected by this deployment. It + must match the pod template's labels. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a set + of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + strategy: + description: The deployment strategy to use to replace existing pods with + new ones. + properties: + rollingUpdate: + description: 'Rolling update config params. Present only if DeploymentStrategyType + = RollingUpdate. --- TODO: Update this to follow our convention + for oneOf, whatever we decide it to be.' + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that can be scheduled + above the desired number of pods. Value can be an absolute number + (ex: 5) or a percentage of desired pods (ex: 10%). This can + not be 0 if MaxUnavailable is 0. Absolute number is calculated + from percentage by rounding up. Defaults to 25%. Example: when + this is set to 30%, the new ReplicaSet can be scaled up immediately + when the rolling update starts, such that the total number of + old and new pods do not exceed 130% of desired pods. Once old + pods have been killed, new ReplicaSet can be scaled up further, + ensuring that total number of pods running at any time during + the update is at most 130% of desired pods.' + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: 'The maximum number of pods that can be unavailable + during the update. Value can be an absolute number (ex: 5) or + a percentage of desired pods (ex: 10%). Absolute number is calculated + from percentage by rounding down. This can not be 0 if MaxSurge + is 0. Defaults to 25%. Example: when this is set to 30%, the + old ReplicaSet can be scaled down to 70% of desired pods immediately + when the rolling update starts. Once new pods are ready, old + ReplicaSet can be scaled down further, followed by scaling up + the new ReplicaSet, ensuring that the total number of pods available + at all times during the update is at least 70% of desired pods.' + x-kubernetes-int-or-string: true + type: object + type: + description: Type of deployment. Can be "Recreate" or "RollingUpdate". + Default is RollingUpdate. + type: string + type: object + template: + description: Template describes the pods that will be created. + properties: + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'Specification of the desired behavior of the pod. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + activeDeadlineSeconds: + description: Optional duration in seconds the pod may be active + on the node relative to StartTime before the system will actively + try to mark it failed and kill associated containers. Value + must be a positive integer. + format: int64 + type: integer + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a + service account token should be automatically mounted. + type: boolean + containers: + description: List of containers belonging to the pod. Containers + cannot currently be added or removed. There must be at least + one container in a Pod. Cannot be updated. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker image''s + CMD is used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s environment. + If a variable cannot be resolved, the reference in the + input string will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). Escaped + references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The docker image''s ENTRYPOINT is used if this is not + provided. Variable references $(VAR_NAME) are expanded + using the container''s environment. If a variable cannot + be resolved, the reference in the input string will be + unchanged. The $(VAR_NAME) syntax can be escaped with + a double $$, ie: $$(VAR_NAME). Escaped references will + never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the + container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are + expanded using the previous defined environment + variables in the container and any service environment + variables. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + - name + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as + an event when the container is starting. When a key exists + in multiple sources, the value associated with the last + source will take precedence. Values defined by an Env + with a duplicate key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + required: + - name + type: object + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + required: + - name + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after + a container is created. If the handler fails, the + container is terminated and restarted according to + its restart policy. Other management of the container + blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a + container is terminated due to an API request or management + event such as liveness/startup probe failure, preemption, + resource contention, etc. The handler is not called + if the container crashes or exits. The reason for + termination is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop hooked + is executed. Regardless of the outcome of the handler, + the container will eventually terminate within the + Pod''s termination grace period. Other management + of the container blocks until the hook completes or + until the termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional information + about the network connections a container uses, but is + primarily informational. Not specifying a port here DOES + NOT prevent that port from being exposed. Any port which + is listening on the default "0.0.0.0" address inside a + container will be accessible from the network. Cannot + be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: Number of port to expose on the pod's + IP address. This must be a valid port number, 0 + < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: Number of port to expose on the host. + If specified, this must be a valid port number, + 0 < x < 65536. If HostNetwork is specified, this + must match ContainerPort. Most containers do not + need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a + pod must have a unique name. Name for the port that + can be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, + or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the + probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run with. + More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent + process. This bool directly controls if the no_new_privs + flag will be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when running + containers. Defaults to the default set of capabilities + granted by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent + to root on the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type of proc mount + to use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the + container. If unspecified, the container runtime will + allocate a random SELinux context for each container. May + also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options from the + PodSecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed + until this completes successfully. If this probe fails, + the Pod will be restarted, just as if the livenessProbe + failed. This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it might + take a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. This is + a beta feature enabled by the StartupProbe feature flag. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, + reads from stdin in the container will always result in + EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close + the stdin channel after it has been opened by a single + attach. When stdin is true the stdin stream will remain + open across multiple attach sessions. If stdinOnce is + set to true, stdin is opened on container start, is empty + until the first client attaches to stdin, and then remains + open and accepts data until the client disconnects, at + which time stdin is closed and remains closed until the + container is restarted. If this flag is false, a container + processes that reads from stdin will never receive an + EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which + the container''s termination message will be written is + mounted into the container''s filesystem. Message written + is intended to be brief final status, such as an assertion + failure message. Will be truncated by the node if greater + than 4096 bytes. The total message length across all containers + will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should + be populated. File will use the contents of terminationMessagePath + to populate the container status message on both success + and failure. FallbackToLogsOnError will use the last chunk + of container log output if the termination message file + is empty and the container exited with an error. The log + output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY + for itself, also requires 'stdin' to be true. Default + is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the + container's volume should be mounted. Defaults to + "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from + which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable + references $(VAR_NAME) are expanded using the container's + environment. Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + description: Specifies the DNS parameters of a pod. Parameters + specified here will be merged to the generated DNS configuration + based on DNSPolicy. + properties: + nameservers: + description: A list of DNS name server IP addresses. This + will be appended to the base nameservers generated from + DNSPolicy. Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. This will be + merged with the base options generated from DNSPolicy. Duplicated + entries will be removed. Resolution options given in Options + will override those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated + from DNSPolicy. Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Set DNS policy for the pod. Defaults to "ClusterFirst". + Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', + 'Default' or 'None'. DNS parameters given in DNSConfig will + be merged with the policy selected with DNSPolicy. To have DNS + options set along with hostNetwork, you have to specify DNS + policy explicitly to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates whether information + about services should be injected into pod''s environment variables, + matching the syntax of Docker links. Optional: Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers run in this pod. Ephemeral + containers may be run in an existing pod to perform user-initiated + actions such as debugging. This list cannot be specified when + creating a pod, and it cannot be modified by updating the pod + spec. In order to add an ephemeral container to an existing + pod, use the pod's ephemeralcontainers subresource. This field + is alpha-level and is only honored by servers that enable the + EphemeralContainers feature. + items: + description: An EphemeralContainer is a container that may be + added temporarily to an existing pod for user-initiated activities + such as debugging. Ephemeral containers have no resource or + scheduling guarantees, and they will not be restarted when + they exit or when a pod is removed or restarted. If an ephemeral + container causes a pod to exceed its resource allocation, + the pod may be evicted. Ephemeral containers may not be added + by directly updating the pod spec. They must be added via + the pod's ephemeralcontainers subresource, and they will appear + in the pod spec once added. This is an alpha feature enabled + by the EphemeralContainers feature flag. + properties: + args: + description: 'Arguments to the entrypoint. The docker image''s + CMD is used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s environment. + If a variable cannot be resolved, the reference in the + input string will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). Escaped + references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The docker image''s ENTRYPOINT is used if this is not + provided. Variable references $(VAR_NAME) are expanded + using the container''s environment. If a variable cannot + be resolved, the reference in the input string will be + unchanged. The $(VAR_NAME) syntax can be escaped with + a double $$, ie: $$(VAR_NAME). Escaped references will + never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the + container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are + expanded using the previous defined environment + variables in the container and any service environment + variables. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + - name + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as + an event when the container is starting. When a key exists + in multiple sources, the value associated with the last + source will take precedence. Values defined by an Env + with a duplicate key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + required: + - name + type: object + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + required: + - name + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed for ephemeral containers. + properties: + postStart: + description: 'PostStart is called immediately after + a container is created. If the handler fails, the + container is terminated and restarted according to + its restart policy. Other management of the container + blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a + container is terminated due to an API request or management + event such as liveness/startup probe failure, preemption, + resource contention, etc. The handler is not called + if the container crashes or exits. The reason for + termination is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop hooked + is executed. Regardless of the outcome of the handler, + the container will eventually terminate within the + Pod''s termination grace period. Other management + of the container blocks until the hook completes or + until the termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Probes are not allowed for ephemeral containers. + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the ephemeral container specified as + a DNS_LABEL. This name must be unique among all containers, + init containers and ephemeral containers. + type: string + ports: + description: Ports are not allowed for ephemeral containers. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: Number of port to expose on the pod's + IP address. This must be a valid port number, 0 + < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: Number of port to expose on the host. + If specified, this must be a valid port number, + 0 < x < 65536. If HostNetwork is specified, this + must match ContainerPort. Most containers do not + need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a + pod must have a unique name. Name for the port that + can be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, + or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + readinessProbe: + description: Probes are not allowed for ephemeral containers. + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: Resources are not allowed for ephemeral containers. + Ephemeral containers use spare resources already allocated + to the pod. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: SecurityContext is not allowed for ephemeral + containers. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent + process. This bool directly controls if the no_new_privs + flag will be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when running + containers. Defaults to the default set of capabilities + granted by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent + to root on the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type of proc mount + to use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the + container. If unspecified, the container runtime will + allocate a random SELinux context for each container. May + also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options from the + PodSecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: Probes are not allowed for ephemeral containers. + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, + reads from stdin in the container will always result in + EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close + the stdin channel after it has been opened by a single + attach. When stdin is true the stdin stream will remain + open across multiple attach sessions. If stdinOnce is + set to true, stdin is opened on container start, is empty + until the first client attaches to stdin, and then remains + open and accepts data until the client disconnects, at + which time stdin is closed and remains closed until the + container is restarted. If this flag is false, a container + processes that reads from stdin will never receive an + EOF. Default is false + type: boolean + targetContainerName: + description: If set, the name of the container from PodSpec + that this ephemeral container targets. The ephemeral container + will be run in the namespaces (IPC, PID, etc) of this + container. If not set then the ephemeral container is + run in whatever namespaces are shared for the pod. Note + that the container runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which the file to which + the container''s termination message will be written is + mounted into the container''s filesystem. Message written + is intended to be brief final status, such as an assertion + failure message. Will be truncated by the node if greater + than 4096 bytes. The total message length across all containers + will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should + be populated. File will use the contents of terminationMessagePath + to populate the container status message on both success + and failure. FallbackToLogsOnError will use the last chunk + of container log output if the termination message file + is empty and the container exited with an error. The log + output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY + for itself, also requires 'stdin' to be true. Default + is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the + container's volume should be mounted. Defaults to + "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from + which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable + references $(VAR_NAME) are expanded using the container's + environment. Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + description: HostAliases is an optional list of hosts and IPs + that will be injected into the pod's hosts file if specified. + This is only valid for non-hostNetwork pods. + items: + description: HostAlias holds the mapping between IP and hostnames + that will be injected as an entry in the pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + description: 'Use the host''s ipc namespace. Optional: Default + to false.' + type: boolean + hostNetwork: + description: Host networking requested for this pod. Use the host's + network namespace. If this option is set, the ports that will + be used must be specified. Default to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. Optional: Default + to false.' + type: boolean + hostname: + description: Specifies the hostname of the Pod If not specified, + the pod's hostname will be set to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional list of references + to secrets in the same namespace to use for pulling any of the + images used by this PodSpec. If specified, these secrets will + be passed to individual puller implementations for them to use. + For example, in the case of docker, only DockerConfig type secrets + are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: 'List of initialization containers belonging to the + pod. Init containers are executed in order prior to containers + being started. If any init container fails, the pod is considered + to have failed and is handled according to its restartPolicy. + The name for an init container or normal container must be unique + among all containers. Init containers may not have Lifecycle + actions, Readiness probes, Liveness probes, or Startup probes. + The resourceRequirements of an init container are taken into + account during scheduling by finding the highest request/limit + for each resource type, and then using the max of of that value + or the sum of the normal containers. Limits are applied to init + containers in a similar fashion. Init containers cannot currently + be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker image''s + CMD is used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s environment. + If a variable cannot be resolved, the reference in the + input string will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). Escaped + references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The docker image''s ENTRYPOINT is used if this is not + provided. Variable references $(VAR_NAME) are expanded + using the container''s environment. If a variable cannot + be resolved, the reference in the input string will be + unchanged. The $(VAR_NAME) syntax can be escaped with + a double $$, ie: $$(VAR_NAME). Escaped references will + never be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the + container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are + expanded using the previous defined environment + variables in the container and any service environment + variables. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + - name + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, metadata.labels, + metadata.annotations, spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as + an event when the container is starting. When a key exists + in multiple sources, the value associated with the last + source will take precedence. Values defined by an Env + with a duplicate key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + required: + - name + type: object + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + required: + - name + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after + a container is created. If the handler fails, the + container is terminated and restarted according to + its restart policy. Other management of the container + blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a + container is terminated due to an API request or management + event such as liveness/startup probe failure, preemption, + resource contention, etc. The handler is not called + if the container crashes or exits. The reason for + termination is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop hooked + is executed. Regardless of the outcome of the handler, + the container will eventually terminate within the + Pod''s termination grace period. Other management + of the container blocks until the hook completes or + until the termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional information + about the network connections a container uses, but is + primarily informational. Not specifying a port here DOES + NOT prevent that port from being exposed. Any port which + is listening on the default "0.0.0.0" address inside a + container will be accessible from the network. Cannot + be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: Number of port to expose on the pod's + IP address. This must be a valid port number, 0 + < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: Number of port to expose on the host. + If specified, this must be a valid port number, + 0 < x < 65536. If HostNetwork is specified, this + must match ContainerPort. Most containers do not + need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a + pod must have a unique name. Name for the port that + can be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, + or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the + probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run with. + More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent + process. This bool directly controls if the no_new_privs + flag will be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when running + containers. Defaults to the default set of capabilities + granted by the container runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent + to root on the host. Defaults to false. + type: boolean + procMount: + description: procMount denotes the type of proc mount + to use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the + container. If unspecified, the container runtime will + allocate a random SELinux context for each container. May + also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options from the + PodSecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed + until this completes successfully. If this probe fails, + the Pod will be restarted, just as if the livenessProbe + failed. This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it might + take a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. This is + a beta feature enabled by the StartupProbe feature flag. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following should + be specified. Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: implement + a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, + reads from stdin in the container will always result in + EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close + the stdin channel after it has been opened by a single + attach. When stdin is true the stdin stream will remain + open across multiple attach sessions. If stdinOnce is + set to true, stdin is opened on container start, is empty + until the first client attaches to stdin, and then remains + open and accepts data until the client disconnects, at + which time stdin is closed and remains closed until the + container is restarted. If this flag is false, a container + processes that reads from stdin will never receive an + EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which + the container''s termination message will be written is + mounted into the container''s filesystem. Message written + is intended to be brief final status, such as an assertion + failure message. Will be truncated by the node if greater + than 4096 bytes. The total message length across all containers + will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should + be populated. File will use the contents of terminationMessagePath + to populate the container status message on both success + and failure. FallbackToLogsOnError will use the last chunk + of container log output if the termination message file + is empty and the container exited with an error. The log + output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY + for itself, also requires 'stdin' to be true. Default + is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the + container's volume should be mounted. Defaults to + "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from + which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable + references $(VAR_NAME) are expanded using the container's + environment. Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + description: NodeName is a request to schedule this pod onto a + specific node. If it is non-empty, the scheduler simply schedules + this pod onto that node, assuming that it fits resource requirements. + type: string + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: + https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Overhead represents the resource overhead associated + with running a pod for a given RuntimeClass. This field will + be autopopulated at admission time by the RuntimeClass admission + controller. If the RuntimeClass admission controller is enabled, + overhead must not be set in Pod create requests. The RuntimeClass + admission controller will reject Pod create requests which have + the overhead already set. If RuntimeClass is configured and + selected in the PodSpec, Overhead will be set to the value defined + in the corresponding RuntimeClass, otherwise it will remain + unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + This field is alpha-level as of Kubernetes v1.16, and is only + honored by servers that enable the PodOverhead feature.' + type: object + preemptionPolicy: + description: PreemptionPolicy is the Policy for preempting pods + with lower priority. One of Never, PreemptLowerPriority. Defaults + to PreemptLowerPriority if unset. This field is alpha-level + and is only honored by servers that enable the NonPreemptingPriority + feature. + type: string + priority: + description: The priority value. Various system components use + this field to find the priority of the pod. When Priority Admission + Controller is enabled, it prevents users from setting this field. + The admission controller populates this field from PriorityClassName. + The higher the value, the higher the priority. + format: int32 + type: integer + priorityClassName: + description: If specified, indicates the pod's priority. "system-node-critical" + and "system-cluster-critical" are two special keywords which + indicate the highest priorities with the former being the highest + priority. Any other name must be defined by creating a PriorityClass + object with that name. If not specified, the pod priority will + be default or zero if there is no default. + type: string + readinessGates: + description: 'If specified, all readiness gates will be evaluated + for pod readiness. A pod is ready when all its containers are + ready AND all conditions specified in the readiness gates have + status equal to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' + items: + description: PodReadinessGate contains the reference to a pod + condition + properties: + conditionType: + description: ConditionType refers to a condition in the + pod's condition list with matching type. + type: string + required: + - conditionType + type: object + type: array + restartPolicy: + description: 'Restart policy for all containers within the pod. + One of Always, OnFailure, Never. Default to Always. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a RuntimeClass object + in the node.k8s.io group, which should be used to run this pod. If + no RuntimeClass resource matches the named class, the pod will + not be run. If unset or empty, the "legacy" RuntimeClass will + be used, which is an implicit class with an empty definition + that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + This is a beta feature as of Kubernetes v1.14.' + type: string + schedulerName: + description: If specified, the pod will be dispatched by specified + scheduler. If not specified, the pod will be dispatched by default + scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level security attributes + and common container settings. Optional: Defaults to empty. See + type description for default values of each field.' + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified defaults to "Always".' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + serviceAccount: + description: 'DeprecatedServiceAccount is a depreciated alias + for ServiceAccountName. Deprecated: Use serviceAccountName instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name of the ServiceAccount + to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + shareProcessNamespace: + description: 'Share a single process namespace between all of + the containers in a pod. When this is set containers will be + able to view and signal processes from other containers in the + same pod, and the first process in each container will not be + assigned PID 1. HostPID and ShareProcessNamespace cannot both + be set. Optional: Default to false.' + type: boolean + subdomain: + description: If specified, the fully qualified Pod hostname will + be "...svc.". + If not specified, the pod will not have a domainname at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully. May be decreased in delete request. Value must be + non-negative integer. The value zero indicates delete immediately. + If this value is nil, the default grace period will be used + instead. The grace period is the duration in seconds after the + processes running in the pod are sent a termination signal and + the time when the processes are forcibly halted with a kill + signal. Set this value longer than the expected cleanup time + for your process. Defaults to 30 seconds. + format: int64 + type: integer + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a group of + pods ought to spread across topology domains. Scheduler will + schedule pods in a way which abides by the constraints. This + field is only honored by clusters that enable the EvenPodsSpread + feature. All topologySpreadConstraints are ANDed. + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine + the number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods + may be unevenly distributed. It''s the maximum permitted + difference between the number of matching pods in any + two topology domains of a given topology type. For example, + in a 3-zone cluster, MaxSkew is set to 1, and pods with + the same labelSelector spread as 1/1/0: | zone1 | zone2 + | zone3 | | P | P | | - if MaxSkew is 1, + incoming pod can only be scheduled to zone3 to become + 1/1/1; scheduling it onto zone1(zone2) would make the + ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - + if MaxSkew is 2, incoming pod can be scheduled onto any + zone. It''s a required field. Default value is 1 and 0 + is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes + that have a label with this key and identical values are + considered to be in the same topology. We consider each + as a "bucket", and try to put balanced number + of pods into each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with + a pod if it doesn''t satisfy the spread constraint. - + DoNotSchedule (default) tells the scheduler not to schedule + it - ScheduleAnyway tells the scheduler to still schedule + it It''s considered as "Unsatisfiable" if and only if + placing incoming pod on any topology violates "MaxSkew". + For example, in a 3-zone cluster, MaxSkew is set to 1, + and pods with the same labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | | P P P | P | P | If + WhenUnsatisfiable is set to DoNotSchedule, incoming pod + can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) + as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). + In other words, the cluster can still be imbalanced, but + scheduler won''t make it *more* imbalanced. It''s a required + field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + description: 'List of volumes that can be mounted by containers + belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + items: + description: Volume represents a named volume in a pod that + may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you + want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property + empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the + default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount + on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read + Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if + unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob + disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set). defaults + to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure + Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host + that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of + Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather + than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key + ring for User, default is /etc/ceph/user.secret More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the + authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + user: + description: 'Optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + volumeID: + description: 'volume id used to identify the volume + in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should + populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 and 0777. + Defaults to 0644. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be + projected into the volume as a file whose name is + the key and content is the value. If specified, the + listed keys will be projected into the specified paths, + and unlisted keys will not be present. If a key is + specified which is not present in the ConfigMap, the + volume setup will error unless it is marked optional. + Paths must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this + file, must be a value between 0 and 0777. If + not specified, the volume defaultMode will be + used. This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to + map the key to. May not be an absolute path. + May not contain the path element '..'. May not + start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys + must be defined + type: boolean + required: + - name + type: object + csi: + description: CSI (Container Storage Interface) represents + storage that is handled by an external CSI driver (Alpha + feature). + properties: + driver: + description: Driver is the name of the CSI driver that + handles this volume. Consult with your admin for the + correct name as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed + to the associated CSI driver which will determine + the default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to + the secret object containing sensitive information + to pass to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the + secret object contains more than one secret, all secret + references are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + readOnly: + description: Specifies a read-only configuration for + the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. Consult + your driver's documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the + pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 and 0777. + Defaults to 0644. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the + pod: only annotations, labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits to use on this + file, must be a value between 0 and 0777. If + not specified, the volume defaultMode will be + used. This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must not + be absolute or contain the ''..'' path. Must + be utf-8 encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back + this directory. The default is "" which means to use + the node''s default medium. Must be an empty string + (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required + for this EmptyDir volume. The size limit is also applicable + for memory medium. The maximum usage on memory medium + EmptyDir would be the minimum value between the SizeLimit + specified here and the sum of memory limits of all + containers in a pod. The default is nil which means + that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + fc: + description: FC represents a Fibre Channel resource that + is attached to a kubelet's host machine and then exposed + to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if + unspecified. TODO: how do we prevent errors in the + filesystem from compromising the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use + for this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the + secret object containing sensitive information to + pass to the plugin scripts. This may be empty if no + secret object is specified. If the secret object contains + more than one secret, all secrets are passed to the + plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the Flocker + control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should be considered + as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then + exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you + want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify + the partition as "1". Similarly, the volume partition + for /dev/sda is "0" (or you can leave the property + empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. + Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an + InitContainer that clones the repo using git, then mount + the EmptyDir into the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain + or start with '..'. If '.' is supplied, the volume + directory will be the git repository. Otherwise, + if specified, the volume will contain the git repository + in the subdirectory with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on + the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that + details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. Defaults + to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or + directory on the host machine that is directly exposed + to the container. This is generally used for system agents + or other privileged things that are allowed to see the + host machine. Most containers will NOT need this. More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host + directory mounts and who can/can not mount host directories + as read/write.' + properties: + path: + description: 'Path of the directory on the host. If + the path is a symlink, it will follow the link to + the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that + is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you + want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new + iSCSI interface : will + be created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI + transport. Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is + either an IP or ip_addr:port if the port is other + than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that + shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export + to be mounted with read-only permissions. Defaults + to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of + the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host + machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if + unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume + attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to + mount Must be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs". Implicitly inferred + to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. Directories within + the path are not affected by this setting. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along + with other supported volume types + properties: + configMap: + description: information about the configMap data + to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the ConfigMap, the volume setup will + error unless it is marked optional. Paths + must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to + use on this file, must be a value + between 0 and 0777. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the + file to map the key to. May not be + an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + required: + - name + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits to + use on this file, must be a value + between 0 and 0777. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of + the container: only resources limits + and requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data + to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the volume + as a file whose name is the key and content + is the value. If specified, the listed keys + will be projected into the specified paths, + and unlisted keys will not be present. If + a key is specified which is not present + in the Secret, the volume setup will error + unless it is marked optional. Paths must + be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to + use on this file, must be a value + between 0 and 0777. If not specified, + the volume defaultMode will be used. + This might be in conflict with other + options that affect the file mode, + like fsGroup, and the result can be + other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the + file to map the key to. May not be + an absolute path. May not contain + the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - name + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience + of the token. A recipient of a token must + identify itself with an identifier specified + in the audience of the token, and otherwise + should reject the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, + the kubelet volume plugin will proactively + rotate the service account token. The kubelet + will start trying to rotate the token if + the token is older than 80 percent of its + time to live or if the token is older than + 24 hours.Defaults to 1 hour and must be + at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to + the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is + no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults + to false. + type: boolean + registry: + description: Registry represents a single or multiple + Quobyte Registry services specified as a string as + host:port pair (multiple entries are separated with + commas) which acts as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned Quobyte + volumes, value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to + serivceaccount user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount + on the host that shares a pod''s lifetime. More info: + https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you + want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + user: + description: 'The rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain + for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for + ScaleIO user and other sensitive information. If this + is not provided, Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume + should be ThickProvisioned or ThinProvisioned. Default + is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with + the protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in + the ScaleIO system that is associated with this volume + source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 and 0777. + Defaults to 0644. Directories within the path are + not affected by this setting. This might be in conflict + with other options that affect the file mode, like + fsGroup, and the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and + content is the value. If specified, the listed keys + will be projected into the specified paths, and unlisted + keys will not be present. If a key is specified which + is not present in the Secret, the volume setup will + error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start + with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this + file, must be a value between 0 and 0777. If + not specified, the volume defaultMode will be + used. This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to + map the key to. May not be an absolute path. + May not contain the path element '..'. May not + start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys + must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if + unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for + obtaining the StorageOS API credentials. If not specified, + default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + required: + - name + type: object + volumeName: + description: VolumeName is the human-readable name of + the StorageOS volume. Volume names are only unique + within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of + the volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows + the Kubernetes name scoping to be mirrored within + StorageOS for tighter integration. Set VolumeName + to any name to override the default behaviour. Set + to "default" if you are not using namespaces within + StorageOS. Namespaces that do not pre-exist within + StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if + unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) + profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) + profile name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + required: + - containers + type: object + type: object + required: + - selector + - template + type: object + status: + description: Most recently observed status of the Deployment. + properties: + availableReplicas: + description: Total number of available pods (ready for at least minReadySeconds) + targeted by this deployment. + format: int32 + type: integer + collisionCount: + description: Count of hash collisions for the Deployment. The Deployment + controller uses this field as a collision avoidance mechanism when it + needs to create the name for the newest ReplicaSet. + format: int32 + type: integer + conditions: + description: Represents the latest available observations of a deployment's + current state. + items: + description: DeploymentCondition describes the state of a deployment + at a certain point. + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about the + transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of deployment condition. + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: The generation observed by the deployment controller. + format: int64 + type: integer + readyReplicas: + description: Total number of ready pods targeted by this deployment. + format: int32 + type: integer + replicas: + description: Total number of non-terminated pods targeted by this deployment + (their labels match the selector). + format: int32 + type: integer + unavailableReplicas: + description: Total number of unavailable pods targeted by this deployment. + This is the total number of pods that are still required for the deployment + to have 100% available capacity. They may either be pods that are running + but not yet available or pods that still have not been created. + format: int32 + type: integer + updatedReplicas: + description: Total number of non-terminated pods targeted by this deployment + that have the desired template spec. + format: int32 + type: integer + type: object + type: object + plural: deployments + schemaUpdateStrategy: UpdateUnpublished + scope: Namespaced + singular: deployment + subResources: + - name: scale + - name: status +status: {} \ No newline at end of file diff --git a/internal/testdata/apiresourceimport.yaml b/internal/testdata/apiresourceimport.yaml new file mode 100644 index 00000000..b213becc --- /dev/null +++ b/internal/testdata/apiresourceimport.yaml @@ -0,0 +1,280 @@ +types: +- name: apiresourceimport + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + map: + fields: + - name: categories + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: columnDefinitions + type: + list: + elementType: + map: + fields: + - name: description + type: + scalar: string + - name: format + type: + scalar: string + - name: jsonPath + type: + scalar: string + - name: name + type: + scalar: string + - name: priority + type: + scalar: numeric + - name: type + type: + scalar: string + elementRelationship: atomic + - name: groupVersion + type: + map: + fields: + - name: group + type: + scalar: string + - name: version + type: + scalar: string + - name: kind + type: + scalar: string + - name: listKind + type: + scalar: string + - name: location + type: + scalar: string + - name: openAPIV3Schema + type: + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable + - name: plural + type: + scalar: string + - name: schemaUpdateStrategy + type: + scalar: string + - name: scope + type: + scalar: string + - name: shortNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: singular + type: + scalar: string + - name: subResources + type: + list: + elementType: + map: + fields: + - name: name + type: + scalar: string + elementRelationship: atomic + - name: status + type: + map: + fields: + - name: conditions + type: + list: + elementType: + map: + fields: + - name: lastTransitionTime + type: + scalar: string + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 + map: + elementType: + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +- name: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + map: + fields: + - name: groupVersion + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldsType + type: + scalar: string + - name: fieldsV1 + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 + - name: manager + type: + scalar: string + - name: operation + type: + scalar: string + - name: subresource + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + map: + fields: + - name: annotations + type: + map: + elementType: + scalar: string + - name: clusterName + type: + scalar: string + - name: creationTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deletionGracePeriodSeconds + type: + scalar: numeric + - name: deletionTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: generateName + type: + scalar: string + - name: generation + type: + scalar: numeric + - name: labels + type: + map: + elementType: + scalar: string + - name: managedFields + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry + elementRelationship: atomic + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: ownerReferences + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + elementRelationship: associative + keys: + - uid +- name: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: blockOwnerDeletion + type: + scalar: boolean + - name: controller + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: uid + type: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Time + scalar: untyped +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable diff --git a/merge/real_test.go b/merge/real_test.go index d994c789..81098588 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -46,6 +46,15 @@ var k8s = func() Parser { return parser }() +var apiresourceimport = func() Parser { + s := read(testdata("apiresourceimport.yaml")) + parser, err := typed.NewParser(typed.YAMLObject(s)) + if err != nil { + panic(err) + } + return parser +}() + func BenchmarkOperations(b *testing.B) { benches := []struct { parseType typed.ParseableType @@ -67,6 +76,10 @@ func BenchmarkOperations(b *testing.B) { parseType: k8s.Type("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition"), filename: "prometheus-crd.yaml", }, + { + parseType: apiresourceimport.Type("apiresourceimport"), + filename: "apiresourceimport-cr.yaml", + }, } for _, bench := range benches { From 478fc281120156294e21e2bf30227ae7b99e19d3 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Mon, 25 Jul 2022 14:20:57 -0700 Subject: [PATCH 020/105] fix nil schema type not being deduced to whatever the other operand is --- typed/merge.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/typed/merge.go b/typed/merge.go index 75244ef6..91364408 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -80,7 +80,12 @@ func (w *mergingWalker) merge(prefixFn func() string) (errs ValidationErrors) { alhs := deduceAtom(a, w.lhs) arhs := deduceAtom(a, w.rhs) - if alhs.Equals(&arhs) { + + // deduceAtom does not fix the type for nil values + // nil is a wildcard and will accept whatever form the other operand takes + if w.rhs == nil { + errs = append(errs, handleAtom(alhs, w.typeRef, w)...) + } else if w.lhs == nil || alhs.Equals(&arhs) { errs = append(errs, handleAtom(arhs, w.typeRef, w)...) } else { w2 := *w From 00b6b52d9465f6866c162a01aa26ac40e7cb1d70 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Tue, 21 Jun 2022 18:07:18 -0700 Subject: [PATCH 021/105] add field-level element relationship which overrides referred type --- merge/field_level_overrides_test.go | 262 ++++++++++++++++++++++++++++ schema/elements.go | 83 ++++++++- schema/elements_test.go | 15 ++ schema/equals.go | 3 + schema/schemaschema.go | 3 + typed/helpers.go | 6 +- typed/helpers_test.go | 47 +++++ 7 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 merge/field_level_overrides_test.go create mode 100644 typed/helpers_test.go diff --git a/merge/field_level_overrides_test.go b/merge/field_level_overrides_test.go new file mode 100644 index 00000000..75ac3256 --- /dev/null +++ b/merge/field_level_overrides_test.go @@ -0,0 +1,262 @@ +package merge_test + +import ( + "testing" + + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +func TestFieldLevelOverrides(t *testing.T) { + var overrideStructTypeParser = func() fixture.Parser { + parser, err := typed.NewParser(` + types: + - name: type + map: + fields: + - name: associativeListReference + type: + namedType: associativeList + elementRelationship: atomic + - name: separableInlineList + type: + list: + elementType: + scalar: numeric + elementRelationship: atomic + elementRelationship: associative + - name: separableMapReference + type: + namedType: atomicMap + elementRelationship: separable + - name: atomicMapReference + type: + namedType: unspecifiedMap + elementRelationship: atomic + + - name: associativeList + list: + elementType: + namedType: unspecifiedMap + elementRelationship: atomic + elementRelationship: associative + keys: + - name + - name: unspecifiedMap + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: numeric + - name: atomicMap + map: + elementRelationship: atomic + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: numeric + `) + if err != nil { + panic(err) + } + return fixture.SameVersionParser{T: parser.Type("type")} + }() + + tests := map[string]fixture.TestCase{ + "test_override_atomic_map_with_separable": { + // Test that a reference with an separable override to an atomic type + // is treated as separable + Ops: []fixture.Operation{ + fixture.Apply{ + Manager: "apply_one", + Object: ` + separableMapReference: + name: a + `, + APIVersion: "v1", + }, + fixture.Apply{ + Manager: "apply_two", + Object: ` + separableMapReference: + value: 2 + `, + APIVersion: "v1", + }, + }, + Object: ` + separableMapReference: + name: a + value: 2 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "apply_one": fieldpath.NewVersionedSet( + _NS( + _P("separableMapReference", "name"), + ), + "v1", + false, + ), + "apply_two": fieldpath.NewVersionedSet( + _NS( + _P("separableMapReference", "value"), + ), + "v1", + false, + ), + }, + }, + "test_override_unspecified_map_with_atomic": { + // Test that a map which has its element relaetionship left as defualt + // (granular) can be overriden to be atomic + Ops: []fixture.Operation{ + fixture.Apply{ + Manager: "apply_one", + Object: ` + atomicMapReference: + name: a + `, + APIVersion: "v1", + }, + fixture.Apply{ + Manager: "apply_two", + Object: ` + atomicMapReference: + value: 2 + `, + APIVersion: "v1", + Conflicts: merge.Conflicts{ + merge.Conflict{Manager: "apply_one", Path: _P("atomicMapReference")}, + }, + }, + fixture.Apply{ + Manager: "apply_one", + Object: ` + atomicMapReference: + name: b + value: 2 + `, + APIVersion: "v1", + }, + }, + Object: ` + atomicMapReference: + name: b + value: 2 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "apply_one": fieldpath.NewVersionedSet( + _NS( + _P("atomicMapReference"), + ), + "v1", + false, + ), + }, + }, + "test_override_associative_list_with_atomic": { + // Test that if a list type is listed associative but referred to as atomic + // that attempting to add to the list fauks + Ops: []fixture.Operation{ + fixture.Apply{ + Manager: "apply_one", + Object: ` + associativeListReference: + - name: a + value: 1 + `, + APIVersion: "v1", + }, + fixture.Apply{ + Manager: "apply_two", + Object: ` + associativeListReference: + - name: b + value: 2 + `, + APIVersion: "v1", + Conflicts: merge.Conflicts{ + merge.Conflict{Manager: "apply_one", Path: _P("associativeListReference")}, + }, + }, + }, + Object: ` + associativeListReference: + - name: a + value: 1 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "apply_one": fieldpath.NewVersionedSet( + _NS( + _P("associativeListReference"), + ), + "v1", + false, + ), + }, + }, + "test_override_inline_atomic_list_with_associative": { + // Tests that an inline atomic list can have its type overridden to be + // associative + Ops: []fixture.Operation{ + fixture.Apply{ + Manager: "apply_one", + Object: ` + separableInlineList: + - 1 + `, + APIVersion: "v1", + }, + fixture.Apply{ + Manager: "apply_two", + Object: ` + separableInlineList: + - 2 + `, + APIVersion: "v1", + }, + }, + Object: ` + separableInlineList: + - 1 + - 2 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "apply_one": fieldpath.NewVersionedSet( + _NS( + _P("separableInlineList", _V(1)), + ), + "v1", + true, + ), + "apply_two": fieldpath.NewVersionedSet( + _NS( + _P("separableInlineList", _V(2)), + ), + "v1", + true, + ), + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if err := test.Test(overrideStructTypeParser); err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/schema/elements.go b/schema/elements.go index 45993aba..b8a75f2b 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -16,7 +16,9 @@ limitations under the License. package schema -import "sync" +import ( + "sync" +) // Schema is a list of named types. // @@ -27,6 +29,15 @@ type Schema struct { once sync.Once m map[string]TypeDef + + // Once used to protect the initialization of `lock` field. + lockOnce sync.Once + // Lock which protects writes to resolvedTypes. Used as pointer so that + // schema may be used as a value type + lock *sync.Mutex + // Cached results of resolving type references to atoms. Only stores + // type references which require fields of Atom to be overriden. + resolvedTypes map[TypeRef]Atom } // A TypeSpecifier references a particular type in a schema. @@ -48,6 +59,12 @@ type TypeRef struct { // Either the name or one member of Atom should be set. NamedType *string `yaml:"namedType,omitempty"` Inlined Atom `yaml:",inline,omitempty"` + + // If this reference refers to a map-type or list-type, this field overrides + // the `ElementRelationship` of the referred type when resolved. + // If this field is nil, then it has no effect. + // See `Map` and `List` for more information about `ElementRelationship` + ElementRelationship *ElementRelationship `yaml:"elementRelationship,omitempty"` } // Atom represents the smallest possible pieces of the type system. @@ -244,20 +261,74 @@ func (s *Schema) FindNamedType(name string) (TypeDef, bool) { return t, ok } +func (s *Schema) resolveNoOverrides(tr TypeRef) (Atom, bool) { + result := Atom{} + + if tr.NamedType != nil { + t, ok := s.FindNamedType(*tr.NamedType) + if !ok { + return Atom{}, false + } + + result = t.Atom + } else { + result = tr.Inlined + } + + return result, true +} + // Resolve is a convenience function which returns the atom referenced, whether // it is inline or named. Returns (Atom{}, false) if the type can't be resolved. // // This allows callers to not care about the difference between a (possibly // inlined) reference and a definition. func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { - if tr.NamedType != nil { - t, ok := s.FindNamedType(*tr.NamedType) - if !ok { + // If this is a plain reference with no overrides, just return the type + if tr.ElementRelationship == nil { + return s.resolveNoOverrides(tr) + } + + // Check to see if we have a cached version of this type + s.lockOnce.Do(func() { + s.lock = &sync.Mutex{} + s.resolvedTypes = make(map[TypeRef]Atom) + }) + + s.lock.Lock() + defer s.lock.Unlock() + + var result Atom + var exists bool + + // Return cached result if available + // If not, calculate result and cache it + if result, exists = s.resolvedTypes[tr]; !exists { + if result, exists = s.resolveNoOverrides(tr); exists { + // Allow field-level electives to override the referred type's modifiers + switch { + case result.Map != nil: + mapCopy := *result.Map + mapCopy.ElementRelationship = *tr.ElementRelationship + result.Map = &mapCopy + case result.List != nil: + listCopy := *result.List + listCopy.ElementRelationship = *tr.ElementRelationship + result.List = &listCopy + case result.Scalar != nil: + return Atom{}, false + default: + return Atom{}, false + } + } else { return Atom{}, false } - return t.Atom, true + + // Save result. If it is nil, that is also recorded as not existing. + s.resolvedTypes[tr] = result } - return tr.Inlined, true + + return result, true } // Clones this instance of Schema into the other diff --git a/schema/elements_test.go b/schema/elements_test.go index ae4e383a..1f05441f 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -90,7 +90,18 @@ func TestFindField(t *testing.T) { func TestResolve(t *testing.T) { existing := "existing" notExisting := "not-existing" + numeric := Numeric + granular := Separable + atomic := Atomic + a := Atom{List: &List{}} + b := Atom{Scalar: &numeric} + + emptyMap := Map{} + atomicMap := Map{ElementRelationship: Atomic} + + emptyList := List{} + atomicList := List{ElementRelationship: Atomic} tests := []struct { testName string @@ -102,6 +113,10 @@ func TestResolve(t *testing.T) { {"noNamedType", nil, TypeRef{Inlined: a}, a, true}, {"notExistingNamedType", nil, TypeRef{NamedType: ¬Existing}, Atom{}, false}, {"existingNamedType", []TypeDef{{Name: existing, Atom: a}}, TypeRef{NamedType: &existing}, a, true}, + {"invalidRelationshipOnScalarType", []TypeDef{{Name: existing, Atom: b}}, TypeRef{NamedType: &existing, ElementRelationship: &granular}, Atom{}, false}, + {"mapElementRelationshipNamed", []TypeDef{{Name: existing, Atom: Atom{Map: &emptyMap}}}, TypeRef{NamedType: &existing, ElementRelationship: &atomic}, Atom{Map: &atomicMap}, true}, + {"mapElementRelationshipInlined", nil, TypeRef{Inlined: Atom{Map: &emptyMap}, ElementRelationship: &atomic}, Atom{Map: &atomicMap}, true}, + {"listElementRelationshipInlined", nil, TypeRef{Inlined: Atom{List: &emptyList}, ElementRelationship: &atomic}, Atom{List: &atomicList}, true}, } for _, tt := range tests { tt := tt diff --git a/schema/equals.go b/schema/equals.go index 4c303eec..b668eff8 100644 --- a/schema/equals.go +++ b/schema/equals.go @@ -52,6 +52,9 @@ func (a *TypeRef) Equals(b *TypeRef) bool { } //return true } + if a.ElementRelationship != b.ElementRelationship { + return false + } return a.Inlined.Equals(&b.Inlined) } diff --git a/schema/schemaschema.go b/schema/schemaschema.go index bb60e2a5..7d64d130 100644 --- a/schema/schemaschema.go +++ b/schema/schemaschema.go @@ -66,6 +66,9 @@ var SchemaSchemaYAML = `types: - name: untyped type: namedType: untyped + - name: elementRelationship + type: + scalar: string - name: scalar scalar: string - name: map diff --git a/typed/helpers.go b/typed/helpers.go index 6b2b2cb4..19c77334 100644 --- a/typed/helpers.go +++ b/typed/helpers.go @@ -105,7 +105,11 @@ type atomHandler interface { func resolveSchema(s *schema.Schema, tr schema.TypeRef, v value.Value, ah atomHandler) ValidationErrors { a, ok := s.Resolve(tr) if !ok { - return errorf("schema error: no type found matching: %v", *tr.NamedType) + typeName := "inlined type" + if tr.NamedType != nil { + typeName = *tr.NamedType + } + return errorf("schema error: no type found matching: %v", typeName) } a = deduceAtom(a, v) diff --git a/typed/helpers_test.go b/typed/helpers_test.go new file mode 100644 index 00000000..d04d4339 --- /dev/null +++ b/typed/helpers_test.go @@ -0,0 +1,47 @@ +package typed_test + +import ( + "strings" + "testing" + + "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +func TestInvalidOverride(t *testing.T) { + // Exercises code path for invalidly specifying a scalar type is atomic + parser, err := typed.NewParser(` + types: + - name: type + map: + fields: + - name: field + type: + scalar: numeric + elementRelationship: atomic + `) + + if err != nil { + t.Fatal(err) + } + + sameVersionParser := fixture.SameVersionParser{T: parser.Type("type")} + + test := fixture.TestCase{ + Ops: []fixture.Operation{ + fixture.Apply{ + Manager: "apply_one", + Object: ` + field: 1 + `, + APIVersion: "v1", + }, + }, + APIVersion: "v1", + } + + if err := test.Test(sameVersionParser); err == nil || + !strings.Contains(err.Error(), "no type found matching: inlined type") { + t.Fatal(err) + } +} From 60b3b658b1164439193c7f1199c63c96c051cbe5 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:52:11 -0700 Subject: [PATCH 022/105] use pointer semantics for schema --- internal/fixture/state.go | 2 +- schema/elements.go | 16 ++++------------ schema/elements_test.go | 4 ++-- schema/equals_test.go | 12 ++++++------ typed/parser.go | 8 +++++--- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 37847858..b37a5e3b 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -477,7 +477,7 @@ func (cs ChangeParser) run(state *State) error { // Swap the schema in for use with the live object so it merges. // If the schema is incompatible, this will fail validation. - liveWithNewSchema, err := typed.AsTyped(state.Live.AsValue(), &cs.Parser.Schema, state.Live.TypeRef()) + liveWithNewSchema, err := typed.AsTyped(state.Live.AsValue(), cs.Parser.Schema, state.Live.TypeRef()) if err != nil { return err } diff --git a/schema/elements.go b/schema/elements.go index b8a75f2b..5d8e72af 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -30,11 +30,7 @@ type Schema struct { once sync.Once m map[string]TypeDef - // Once used to protect the initialization of `lock` field. - lockOnce sync.Once - // Lock which protects writes to resolvedTypes. Used as pointer so that - // schema may be used as a value type - lock *sync.Mutex + lock sync.Mutex // Cached results of resolving type references to atoms. Only stores // type references which require fields of Atom to be overriden. resolvedTypes map[TypeRef]Atom @@ -43,7 +39,7 @@ type Schema struct { // A TypeSpecifier references a particular type in a schema. type TypeSpecifier struct { Type TypeRef `yaml:"type,omitempty"` - Schema Schema `yaml:"schema,omitempty"` + Schema *Schema `yaml:"schema,omitempty"` } // TypeDef represents a named type in a schema. @@ -290,13 +286,9 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { } // Check to see if we have a cached version of this type - s.lockOnce.Do(func() { - s.lock = &sync.Mutex{} - s.resolvedTypes = make(map[TypeRef]Atom) - }) - s.lock.Lock() defer s.lock.Unlock() + s.resolvedTypes = make(map[TypeRef]Atom) var result Atom var exists bool @@ -335,7 +327,7 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { // If other is nil this method does nothing. // If other is already initialized, overwrites it with this instance // Warning: Not thread safe -func (s Schema) CopyInto(dst *Schema) { +func (s *Schema) CopyInto(dst *Schema) { if dst == nil { return } diff --git a/schema/elements_test.go b/schema/elements_test.go index 1f05441f..be802fbf 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -161,7 +161,7 @@ func TestCopyInto(t *testing.T) { theCopy := Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(s, theCopy) { + if !reflect.DeepEqual(&s, &theCopy) { t.Fatal("") } @@ -170,7 +170,7 @@ func TestCopyInto(t *testing.T) { theCopy = Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(s, theCopy) { + if !reflect.DeepEqual(&s, &theCopy) { t.Fatal("") } }) diff --git a/schema/equals_test.go b/schema/equals_test.go index fff292c2..0994a771 100644 --- a/schema/equals_test.go +++ b/schema/equals_test.go @@ -31,10 +31,10 @@ func fuzzInterface(i *interface{}, c fuzz.Continue) { *i = &m } -func (Schema) Generate(rand *rand.Rand, size int) reflect.Value { - s := Schema{} +func (*Schema) Generate(rand *rand.Rand, size int) reflect.Value { + s := &Schema{} f := fuzz.New().RandSource(rand).MaxDepth(4) - f.Fuzz(&s) + f.Fuzz(s) return reflect.ValueOf(s) } @@ -73,13 +73,13 @@ func TestEquals(t *testing.T) { // The "copy known fields" section of these function is to break if folks // add new fields without fixing the Equals function and this test. funcs := []interface{}{ - func(x Schema) bool { - if !x.Equals(&x) { + func(x *Schema) bool { + if !x.Equals(x) { return false } var y Schema y.Types = x.Types - return x.Equals(&y) == reflect.DeepEqual(&x, &y) + return x.Equals(&y) == reflect.DeepEqual(x, &y) }, func(x TypeDef) bool { if !x.Equals(&x) { diff --git a/typed/parser.go b/typed/parser.go index 3949a78f..1e657ef7 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -29,12 +29,14 @@ type YAMLObject string // Parser implements YAMLParser and allows introspecting the schema. type Parser struct { - Schema schema.Schema + Schema *schema.Schema } // create builds an unvalidated parser. func create(s YAMLObject) (*Parser, error) { - p := Parser{} + p := Parser{ + Schema: &schema.Schema{}, + } err := yaml.Unmarshal([]byte(s), &p.Schema) return &p, err } @@ -74,7 +76,7 @@ func (p *Parser) TypeNames() (names []string) { // errors are deferred until a further function is called. func (p *Parser) Type(name string) ParseableType { return ParseableType{ - Schema: &p.Schema, + Schema: p.Schema, TypeRef: schema.TypeRef{NamedType: &name}, } } From 15f722a6518e74851cef699b82f00ee23124857e Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Tue, 26 Jul 2022 15:23:05 -0700 Subject: [PATCH 023/105] add benchmark excercising field-level overrides --- .../k8s-schema-100pct-fieldoverride.yaml | 10903 ++++++++++++++++ .../k8s-schema-10pct-fieldoverride.yaml | 10323 +++++++++++++++ merge/real_test.go | 46 +- 3 files changed, 21260 insertions(+), 12 deletions(-) create mode 100644 internal/testdata/k8s-schema-100pct-fieldoverride.yaml create mode 100644 internal/testdata/k8s-schema-10pct-fieldoverride.yaml diff --git a/internal/testdata/k8s-schema-100pct-fieldoverride.yaml b/internal/testdata/k8s-schema-100pct-fieldoverride.yaml new file mode 100644 index 00000000..96e1a36d --- /dev/null +++ b/internal/testdata/k8s-schema-100pct-fieldoverride.yaml @@ -0,0 +1,10903 @@ +types: +- name: io.k8s.api.admissionregistration.v1alpha1.Initializer + map: + fields: + - name: name + type: + scalar: string + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.Rule + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1alpha1.InitializerConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: initializers + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.Initializer + elementRelationship: associative + keys: + - name + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular +- name: io.k8s.api.admissionregistration.v1alpha1.InitializerConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.InitializerConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular + elementRelationship: granular +- name: io.k8s.api.admissionregistration.v1alpha1.Rule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: apiVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: webhooks + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.Webhook + elementRelationship: associative + keys: + - name +- name: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.admissionregistration.v1beta1.RuleWithOperations + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: apiVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: operations + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1beta1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: webhooks + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.Webhook + elementRelationship: associative + keys: + - name +- name: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.admissionregistration.v1beta1.Webhook + map: + fields: + - name: clientConfig + type: + namedType: io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig + elementRelationship: granular + - name: failurePolicy + type: + scalar: string + - name: name + type: + scalar: string + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.RuleWithOperations + elementRelationship: atomic + - name: sideEffects + type: + scalar: string +- name: io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig + map: + fields: + - name: caBundle + type: + scalar: string + - name: service + type: + namedType: io.k8s.api.admissionregistration.v1beta1.ServiceReference + elementRelationship: granular + - name: url + type: + scalar: string +- name: io.k8s.api.apps.v1.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + elementRelationship: granular + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1.DaemonSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1.DaemonSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1.DaemonSetUpdateStrategy + elementRelationship: granular +- name: io.k8s.api.apps.v1.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.apps.v1.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateDaemonSet + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1.DeploymentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1.DeploymentStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: strategy + type: + namedType: io.k8s.api.apps.v1.DeploymentStrategy + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.apps.v1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateDeployment + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1.ReplicaSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1.ReplicaSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.apps.v1.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.apps.v1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1.StatefulSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1.StatefulSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1.StatefulSetUpdateStrategy + elementRelationship: granular + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + elementRelationship: granular + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.DeploymentRollback + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: rollbackTo + type: + namedType: io.k8s.api.apps.v1beta1.RollbackConfig + elementRelationship: granular + - name: updatedAnnotations + type: + map: + elementType: + scalar: string +- name: io.k8s.api.apps.v1beta1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: rollbackTo + type: + namedType: io.k8s.api.apps.v1beta1.RollbackConfig + elementRelationship: granular + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: strategy + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentStrategy + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta1.RollingUpdateDeployment + elementRelationship: granular + - name: type + type: + scalar: string + unions: + - discriminator: type + fields: + - fieldName: rollingUpdate + discriminatorValue: RollingUpdate +- name: io.k8s.api.apps.v1beta1.RollbackConfig + map: + fields: + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.ScaleStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy + elementRelationship: granular + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1beta1.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + elementRelationship: granular + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: strategy + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentStrategy + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateDeployment + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.ScaleStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetStatus + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy + elementRelationship: granular + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1beta2.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.AuditSink + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec + elementRelationship: granular +- name: io.k8s.api.auditregistration.v1alpha1.AuditSinkList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.auditregistration.v1alpha1.AuditSink + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec + map: + fields: + - name: policy + type: + namedType: io.k8s.api.auditregistration.v1alpha1.Policy + elementRelationship: granular + - name: webhook + type: + namedType: io.k8s.api.auditregistration.v1alpha1.Webhook + elementRelationship: granular +- name: io.k8s.api.auditregistration.v1alpha1.Policy + map: + fields: + - name: level + type: + scalar: string + - name: stages + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.auditregistration.v1alpha1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.Webhook + map: + fields: + - name: clientConfig + type: + namedType: io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig + elementRelationship: granular + - name: throttle + type: + namedType: io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig + elementRelationship: granular +- name: io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig + map: + fields: + - name: caBundle + type: + scalar: string + - name: service + type: + namedType: io.k8s.api.auditregistration.v1alpha1.ServiceReference + elementRelationship: granular + - name: url + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig + map: + fields: + - name: burst + type: + scalar: numeric + - name: qps + type: + scalar: numeric +- name: io.k8s.api.authentication.v1.TokenReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authentication.v1.TokenReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authentication.v1.TokenReviewStatus + elementRelationship: granular +- name: io.k8s.api.authentication.v1.TokenReviewSpec + map: + fields: + - name: token + type: + scalar: string +- name: io.k8s.api.authentication.v1.TokenReviewStatus + map: + fields: + - name: authenticated + type: + scalar: boolean + - name: error + type: + scalar: string + - name: user + type: + namedType: io.k8s.api.authentication.v1.UserInfo + elementRelationship: granular +- name: io.k8s.api.authentication.v1.UserInfo + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: uid + type: + scalar: string + - name: username + type: + scalar: string +- name: io.k8s.api.authentication.v1beta1.TokenReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authentication.v1beta1.TokenReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authentication.v1beta1.TokenReviewStatus + elementRelationship: granular +- name: io.k8s.api.authentication.v1beta1.TokenReviewSpec + map: + fields: + - name: token + type: + scalar: string +- name: io.k8s.api.authentication.v1beta1.TokenReviewStatus + map: + fields: + - name: authenticated + type: + scalar: boolean + - name: error + type: + scalar: string + - name: user + type: + namedType: io.k8s.api.authentication.v1beta1.UserInfo + elementRelationship: granular +- name: io.k8s.api.authentication.v1beta1.UserInfo + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: uid + type: + scalar: string + - name: username + type: + scalar: string +- name: io.k8s.api.authorization.v1.LocalSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1.NonResourceAttributes + map: + fields: + - name: path + type: + scalar: string + - name: verb + type: + scalar: string +- name: io.k8s.api.authorization.v1.NonResourceRule + map: + fields: + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1.ResourceAttributes + map: + fields: + - name: group + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resource + type: + scalar: string + - name: subresource + type: + scalar: string + - name: verb + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.api.authorization.v1.ResourceRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1.SelfSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec + map: + fields: + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1.NonResourceAttributes + elementRelationship: granular + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1.ResourceAttributes + elementRelationship: granular +- name: io.k8s.api.authorization.v1.SelfSubjectRulesReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectRulesReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec + map: + fields: + - name: namespace + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1.NonResourceAttributes + elementRelationship: granular + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1.ResourceAttributes + elementRelationship: granular + - name: uid + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectAccessReviewStatus + map: + fields: + - name: allowed + type: + scalar: boolean + - name: denied + type: + scalar: boolean + - name: evaluationError + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectRulesReviewStatus + map: + fields: + - name: evaluationError + type: + scalar: string + - name: incomplete + type: + scalar: boolean + - name: nonResourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1.NonResourceRule + elementRelationship: atomic + - name: resourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1.ResourceRule + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.LocalSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1beta1.NonResourceAttributes + map: + fields: + - name: path + type: + scalar: string + - name: verb + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.NonResourceRule + map: + fields: + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.ResourceAttributes + map: + fields: + - name: group + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resource + type: + scalar: string + - name: subresource + type: + scalar: string + - name: verb + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.ResourceRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec + map: + fields: + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.NonResourceAttributes + elementRelationship: granular + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.ResourceAttributes + elementRelationship: granular +- name: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec + map: + fields: + - name: namespace + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus + elementRelationship: granular +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: group + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.NonResourceAttributes + elementRelationship: granular + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.ResourceAttributes + elementRelationship: granular + - name: uid + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus + map: + fields: + - name: allowed + type: + scalar: boolean + - name: denied + type: + scalar: boolean + - name: evaluationError + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus + map: + fields: + - name: evaluationError + type: + scalar: string + - name: incomplete + type: + scalar: boolean + - name: nonResourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1beta1.NonResourceRule + elementRelationship: atomic + - name: resourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1beta1.ResourceRule + elementRelationship: atomic +- name: io.k8s.api.autoscaling.v1.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus + elementRelationship: granular +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v1.CrossVersionObjectReference + elementRelationship: granular + - name: targetCPUUtilizationPercentage + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus + map: + fields: + - name: currentCPUUtilizationPercentage + type: + scalar: numeric + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.autoscaling.v1.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.autoscaling.v1.ScaleStatus + elementRelationship: granular +- name: io.k8s.api.autoscaling.v1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.ExternalMetricSource + map: + fields: + - name: metricName + type: + scalar: string + - name: metricSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: targetValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus + map: + fields: + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: currentValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: metricName + type: + scalar: string + - name: metricSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: metrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.MetricSpec + elementRelationship: atomic + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition + elementRelationship: atomic + - name: currentMetrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.MetricStatus + elementRelationship: atomic + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v2beta1.MetricSpec + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta1.ExternalMetricSource + elementRelationship: granular + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta1.ObjectMetricSource + elementRelationship: granular + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta1.PodsMetricSource + elementRelationship: granular + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta1.ResourceMetricSource + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.MetricStatus + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus + elementRelationship: granular + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus + elementRelationship: granular + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta1.PodsMetricStatus + elementRelationship: granular + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.ObjectMetricSource + map: + fields: + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + elementRelationship: granular + - name: targetValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus + map: + fields: + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: currentValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.PodsMetricSource + map: + fields: + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.PodsMetricStatus + map: + fields: + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.ResourceMetricSource + map: + fields: + - name: name + type: + scalar: string + - name: targetAverageUtilization + type: + scalar: numeric + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus + map: + fields: + - name: currentAverageUtilization + type: + scalar: numeric + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.ExternalMetricSource + map: + fields: + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + elementRelationship: granular + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: metrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.MetricSpec + elementRelationship: atomic + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition + elementRelationship: atomic + - name: currentMetrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.MetricStatus + elementRelationship: atomic + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + map: + fields: + - name: name + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.MetricSpec + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta2.ExternalMetricSource + elementRelationship: granular + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta2.ObjectMetricSource + elementRelationship: granular + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta2.PodsMetricSource + elementRelationship: granular + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta2.ResourceMetricSource + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.MetricStatus + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus + elementRelationship: granular + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus + elementRelationship: granular + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta2.PodsMetricStatus + elementRelationship: granular + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.MetricTarget + map: + fields: + - name: averageUtilization + type: + scalar: numeric + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: type + type: + scalar: string + - name: value + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + map: + fields: + - name: averageUtilization + type: + scalar: numeric + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: value + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.ObjectMetricSource + map: + fields: + - name: describedObject + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + elementRelationship: granular + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + elementRelationship: granular + - name: describedObject + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + elementRelationship: granular + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.PodsMetricSource + map: + fields: + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.PodsMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + elementRelationship: granular + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.ResourceMetricSource + map: + fields: + - name: name + type: + scalar: string + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + elementRelationship: granular + - name: name + type: + scalar: string +- name: io.k8s.api.batch.v1.Job + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.batch.v1.JobStatus + elementRelationship: granular +- name: io.k8s.api.batch.v1.JobCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.batch.v1.JobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v1.Job + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.batch.v1.JobSpec + map: + fields: + - name: activeDeadlineSeconds + type: + scalar: numeric + - name: backoffLimit + type: + scalar: numeric + - name: completions + type: + scalar: numeric + - name: manualSelector + type: + scalar: boolean + - name: parallelism + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: ttlSecondsAfterFinished + type: + scalar: numeric +- name: io.k8s.api.batch.v1.JobStatus + map: + fields: + - name: active + type: + scalar: numeric + - name: completionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.batch.v1.JobCondition + elementRelationship: associative + keys: + - type + - name: failed + type: + scalar: numeric + - name: startTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: succeeded + type: + scalar: numeric +- name: io.k8s.api.batch.v1beta1.CronJob + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.batch.v1beta1.CronJobSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.batch.v1beta1.CronJobStatus + elementRelationship: granular +- name: io.k8s.api.batch.v1beta1.CronJobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v1beta1.CronJob + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.batch.v1beta1.CronJobSpec + map: + fields: + - name: concurrencyPolicy + type: + scalar: string + - name: failedJobsHistoryLimit + type: + scalar: numeric + - name: jobTemplate + type: + namedType: io.k8s.api.batch.v1beta1.JobTemplateSpec + elementRelationship: granular + - name: schedule + type: + scalar: string + - name: startingDeadlineSeconds + type: + scalar: numeric + - name: successfulJobsHistoryLimit + type: + scalar: numeric + - name: suspend + type: + scalar: boolean +- name: io.k8s.api.batch.v1beta1.CronJobStatus + map: + fields: + - name: active + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: atomic + - name: lastScheduleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.batch.v1beta1.JobTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec + elementRelationship: granular +- name: io.k8s.api.batch.v2alpha1.CronJob + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.batch.v2alpha1.CronJobSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.batch.v2alpha1.CronJobStatus + elementRelationship: granular +- name: io.k8s.api.batch.v2alpha1.CronJobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v2alpha1.CronJob + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.batch.v2alpha1.CronJobSpec + map: + fields: + - name: concurrencyPolicy + type: + scalar: string + - name: failedJobsHistoryLimit + type: + scalar: numeric + - name: jobTemplate + type: + namedType: io.k8s.api.batch.v2alpha1.JobTemplateSpec + elementRelationship: granular + - name: schedule + type: + scalar: string + - name: startingDeadlineSeconds + type: + scalar: numeric + - name: successfulJobsHistoryLimit + type: + scalar: numeric + - name: suspend + type: + scalar: boolean +- name: io.k8s.api.batch.v2alpha1.CronJobStatus + map: + fields: + - name: active + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: atomic + - name: lastScheduleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.batch.v2alpha1.JobTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec + elementRelationship: granular +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequest + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus + elementRelationship: granular +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition + map: + fields: + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequest + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: request + type: + scalar: string + - name: uid + type: + scalar: string + - name: usages + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: username + type: + scalar: string +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus + map: + fields: + - name: certificate + type: + scalar: string + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition + elementRelationship: atomic +- name: io.k8s.api.coordination.v1beta1.Lease + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.coordination.v1beta1.LeaseSpec + elementRelationship: granular +- name: io.k8s.api.coordination.v1beta1.LeaseList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.coordination.v1beta1.Lease + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.coordination.v1beta1.LeaseSpec + map: + fields: + - name: acquireTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: holderIdentity + type: + scalar: string + - name: leaseDurationSeconds + type: + scalar: numeric + - name: leaseTransitions + type: + scalar: numeric + - name: renewTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular +- name: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: partition + type: + scalar: numeric + - name: readOnly + type: + scalar: boolean + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.Affinity + map: + fields: + - name: nodeAffinity + type: + namedType: io.k8s.api.core.v1.NodeAffinity + elementRelationship: granular + - name: podAffinity + type: + namedType: io.k8s.api.core.v1.PodAffinity + elementRelationship: granular + - name: podAntiAffinity + type: + namedType: io.k8s.api.core.v1.PodAntiAffinity + elementRelationship: granular +- name: io.k8s.api.core.v1.AttachedVolume + map: + fields: + - name: devicePath + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.AzureDiskVolumeSource + map: + fields: + - name: cachingMode + type: + scalar: string + - name: diskName + type: + scalar: string + - name: diskURI + type: + scalar: string + - name: fsType + type: + scalar: string + - name: kind + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.AzureFilePersistentVolumeSource + map: + fields: + - name: readOnly + type: + scalar: boolean + - name: secretName + type: + scalar: string + - name: secretNamespace + type: + scalar: string + - name: shareName + type: + scalar: string +- name: io.k8s.api.core.v1.AzureFileVolumeSource + map: + fields: + - name: readOnly + type: + scalar: boolean + - name: secretName + type: + scalar: string + - name: shareName + type: + scalar: string +- name: io.k8s.api.core.v1.Binding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular +- name: io.k8s.api.core.v1.CSIPersistentVolumeSource + map: + fields: + - name: controllerPublishSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: nodePublishSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: nodeStageSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: readOnly + type: + scalar: boolean + - name: volumeAttributes + type: + map: + elementType: + scalar: string + - name: volumeHandle + type: + scalar: string +- name: io.k8s.api.core.v1.Capabilities + map: + fields: + - name: add + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: drop + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.CephFSPersistentVolumeSource + map: + fields: + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretFile + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.CephFSVolumeSource + map: + fields: + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretFile + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.CinderPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.CinderVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.ClientIPConfig + map: + fields: + - name: timeoutSeconds + type: + scalar: numeric +- name: io.k8s.api.core.v1.ComponentCondition + map: + fields: + - name: error + type: + scalar: string + - name: message + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ComponentStatus + map: + fields: + - name: apiVersion + type: + scalar: string + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ComponentCondition + elementRelationship: associative + keys: + - type + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ComponentStatusList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ComponentStatus + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ConfigMap + map: + fields: + - name: apiVersion + type: + scalar: string + - name: binaryData + type: + map: + elementType: + scalar: string + - name: data + type: + map: + elementType: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ConfigMapEnvSource + map: + fields: + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapKeySelector + map: + fields: + - name: key + type: + scalar: string + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ConfigMap + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ConfigMapNodeConfigSource + map: + fields: + - name: kubeletConfigKey + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.api.core.v1.ConfigMapProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.Container + map: + fields: + - name: args + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: command + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: env + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvVar + elementRelationship: associative + keys: + - name + - name: envFrom + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvFromSource + elementRelationship: atomic + - name: image + type: + scalar: string + - name: imagePullPolicy + type: + scalar: string + - name: lifecycle + type: + namedType: io.k8s.api.core.v1.Lifecycle + elementRelationship: granular + - name: livenessProbe + type: + namedType: io.k8s.api.core.v1.Probe + elementRelationship: granular + - name: name + type: + scalar: string + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerPort + elementRelationship: associative + keys: + - containerPort + - protocol + - name: readinessProbe + type: + namedType: io.k8s.api.core.v1.Probe + elementRelationship: granular + - name: resources + type: + namedType: io.k8s.api.core.v1.ResourceRequirements + elementRelationship: granular + - name: securityContext + type: + namedType: io.k8s.api.core.v1.SecurityContext + elementRelationship: granular + - name: stdin + type: + scalar: boolean + - name: stdinOnce + type: + scalar: boolean + - name: terminationMessagePath + type: + scalar: string + - name: terminationMessagePolicy + type: + scalar: string + - name: tty + type: + scalar: boolean + - name: volumeDevices + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeDevice + elementRelationship: associative + keys: + - devicePath + - name: volumeMounts + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeMount + elementRelationship: associative + keys: + - mountPath + - name: workingDir + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerImage + map: + fields: + - name: names + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: sizeBytes + type: + scalar: numeric +- name: io.k8s.api.core.v1.ContainerPort + map: + fields: + - name: containerPort + type: + scalar: numeric + - name: hostIP + type: + scalar: string + - name: hostPort + type: + scalar: numeric + - name: name + type: + scalar: string + - name: protocol + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerState + map: + fields: + - name: running + type: + namedType: io.k8s.api.core.v1.ContainerStateRunning + elementRelationship: granular + - name: terminated + type: + namedType: io.k8s.api.core.v1.ContainerStateTerminated + elementRelationship: granular + - name: waiting + type: + namedType: io.k8s.api.core.v1.ContainerStateWaiting + elementRelationship: granular +- name: io.k8s.api.core.v1.ContainerStateRunning + map: + fields: + - name: startedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.ContainerStateTerminated + map: + fields: + - name: containerID + type: + scalar: string + - name: exitCode + type: + scalar: numeric + - name: finishedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: signal + type: + scalar: numeric + - name: startedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.ContainerStateWaiting + map: + fields: + - name: message + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerStatus + map: + fields: + - name: containerID + type: + scalar: string + - name: image + type: + scalar: string + - name: imageID + type: + scalar: string + - name: lastState + type: + namedType: io.k8s.api.core.v1.ContainerState + elementRelationship: granular + - name: name + type: + scalar: string + - name: ready + type: + scalar: boolean + - name: restartCount + type: + scalar: numeric + - name: state + type: + namedType: io.k8s.api.core.v1.ContainerState + elementRelationship: granular +- name: io.k8s.api.core.v1.DaemonEndpoint + map: + fields: + - name: Port + type: + scalar: numeric +- name: io.k8s.api.core.v1.DownwardAPIProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeFile + elementRelationship: atomic +- name: io.k8s.api.core.v1.DownwardAPIVolumeFile + map: + fields: + - name: fieldRef + type: + namedType: io.k8s.api.core.v1.ObjectFieldSelector + elementRelationship: granular + - name: mode + type: + scalar: numeric + - name: path + type: + scalar: string + - name: resourceFieldRef + type: + namedType: io.k8s.api.core.v1.ResourceFieldSelector + elementRelationship: granular +- name: io.k8s.api.core.v1.DownwardAPIVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeFile + elementRelationship: atomic +- name: io.k8s.api.core.v1.EmptyDirVolumeSource + map: + fields: + - name: medium + type: + scalar: string + - name: sizeLimit + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular +- name: io.k8s.api.core.v1.EndpointAddress + map: + fields: + - name: hostname + type: + scalar: string + - name: ip + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: targetRef + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular +- name: io.k8s.api.core.v1.EndpointPort + map: + fields: + - name: name + type: + scalar: string + - name: port + type: + scalar: numeric + - name: protocol + type: + scalar: string +- name: io.k8s.api.core.v1.EndpointSubset + map: + fields: + - name: addresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointAddress + elementRelationship: atomic + - name: notReadyAddresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointAddress + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointPort + elementRelationship: atomic +- name: io.k8s.api.core.v1.Endpoints + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: subsets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointSubset + elementRelationship: atomic +- name: io.k8s.api.core.v1.EndpointsList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Endpoints + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.EnvFromSource + map: + fields: + - name: configMapRef + type: + namedType: io.k8s.api.core.v1.ConfigMapEnvSource + elementRelationship: granular + - name: prefix + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretEnvSource + elementRelationship: granular +- name: io.k8s.api.core.v1.EnvVar + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string + - name: valueFrom + type: + namedType: io.k8s.api.core.v1.EnvVarSource + elementRelationship: granular +- name: io.k8s.api.core.v1.EnvVarSource + map: + fields: + - name: configMapKeyRef + type: + namedType: io.k8s.api.core.v1.ConfigMapKeySelector + elementRelationship: granular + - name: fieldRef + type: + namedType: io.k8s.api.core.v1.ObjectFieldSelector + elementRelationship: granular + - name: resourceFieldRef + type: + namedType: io.k8s.api.core.v1.ResourceFieldSelector + elementRelationship: granular + - name: secretKeyRef + type: + namedType: io.k8s.api.core.v1.SecretKeySelector + elementRelationship: granular +- name: io.k8s.api.core.v1.Event + map: + fields: + - name: action + type: + scalar: string + - name: apiVersion + type: + scalar: string + - name: count + type: + scalar: numeric + - name: eventTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: firstTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: involvedObject + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: kind + type: + scalar: string + - name: lastTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: reason + type: + scalar: string + - name: related + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: reportingComponent + type: + scalar: string + - name: reportingInstance + type: + scalar: string + - name: series + type: + namedType: io.k8s.api.core.v1.EventSeries + elementRelationship: granular + - name: source + type: + namedType: io.k8s.api.core.v1.EventSource + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.EventList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Event + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.EventSeries + map: + fields: + - name: count + type: + scalar: numeric + - name: lastObservedTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: state + type: + scalar: string +- name: io.k8s.api.core.v1.EventSource + map: + fields: + - name: component + type: + scalar: string + - name: host + type: + scalar: string +- name: io.k8s.api.core.v1.ExecAction + map: + fields: + - name: command + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.FCVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: readOnly + type: + scalar: boolean + - name: targetWWNs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: wwids + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.FlexPersistentVolumeSource + map: + fields: + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: options + type: + map: + elementType: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular +- name: io.k8s.api.core.v1.FlexVolumeSource + map: + fields: + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: options + type: + map: + elementType: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular +- name: io.k8s.api.core.v1.FlockerVolumeSource + map: + fields: + - name: datasetName + type: + scalar: string + - name: datasetUUID + type: + scalar: string +- name: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: partition + type: + scalar: numeric + - name: pdName + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.GitRepoVolumeSource + map: + fields: + - name: directory + type: + scalar: string + - name: repository + type: + scalar: string + - name: revision + type: + scalar: string +- name: io.k8s.api.core.v1.GlusterfsVolumeSource + map: + fields: + - name: endpoints + type: + scalar: string + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.HTTPGetAction + map: + fields: + - name: host + type: + scalar: string + - name: httpHeaders + type: + list: + elementType: + namedType: io.k8s.api.core.v1.HTTPHeader + elementRelationship: atomic + - name: path + type: + scalar: string + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: scheme + type: + scalar: string +- name: io.k8s.api.core.v1.HTTPHeader + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.Handler + map: + fields: + - name: exec + type: + namedType: io.k8s.api.core.v1.ExecAction + elementRelationship: granular + - name: httpGet + type: + namedType: io.k8s.api.core.v1.HTTPGetAction + elementRelationship: granular + - name: tcpSocket + type: + namedType: io.k8s.api.core.v1.TCPSocketAction + elementRelationship: granular +- name: io.k8s.api.core.v1.HostAlias + map: + fields: + - name: hostnames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: ip + type: + scalar: string +- name: io.k8s.api.core.v1.HostPathVolumeSource + map: + fields: + - name: path + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ISCSIPersistentVolumeSource + map: + fields: + - name: chapAuthDiscovery + type: + scalar: boolean + - name: chapAuthSession + type: + scalar: boolean + - name: fsType + type: + scalar: string + - name: initiatorName + type: + scalar: string + - name: iqn + type: + scalar: string + - name: iscsiInterface + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: portals + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: targetPortal + type: + scalar: string +- name: io.k8s.api.core.v1.ISCSIVolumeSource + map: + fields: + - name: chapAuthDiscovery + type: + scalar: boolean + - name: chapAuthSession + type: + scalar: boolean + - name: fsType + type: + scalar: string + - name: initiatorName + type: + scalar: string + - name: iqn + type: + scalar: string + - name: iscsiInterface + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: portals + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: targetPortal + type: + scalar: string +- name: io.k8s.api.core.v1.KeyToPath + map: + fields: + - name: key + type: + scalar: string + - name: mode + type: + scalar: numeric + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.Lifecycle + map: + fields: + - name: postStart + type: + namedType: io.k8s.api.core.v1.Handler + elementRelationship: granular + - name: preStop + type: + namedType: io.k8s.api.core.v1.Handler + elementRelationship: granular +- name: io.k8s.api.core.v1.LimitRange + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.LimitRangeSpec + elementRelationship: granular +- name: io.k8s.api.core.v1.LimitRangeItem + map: + fields: + - name: default + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: defaultRequest + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: max + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: maxLimitRequestRatio + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: min + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.LimitRangeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LimitRange + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.LimitRangeSpec + map: + fields: + - name: limits + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LimitRangeItem + elementRelationship: atomic +- name: io.k8s.api.core.v1.LoadBalancerIngress + map: + fields: + - name: hostname + type: + scalar: string + - name: ip + type: + scalar: string +- name: io.k8s.api.core.v1.LoadBalancerStatus + map: + fields: + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LoadBalancerIngress + elementRelationship: atomic +- name: io.k8s.api.core.v1.LocalObjectReference + map: + fields: + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.LocalVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.NFSVolumeSource + map: + fields: + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: server + type: + scalar: string +- name: io.k8s.api.core.v1.Namespace + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.NamespaceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.NamespaceStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.NamespaceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Namespace + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.NamespaceSpec + map: + fields: + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NamespaceStatus + map: + fields: + - name: phase + type: + scalar: string +- name: io.k8s.api.core.v1.Node + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.NodeSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.NodeStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeAddress + map: + fields: + - name: address + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.NodeAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PreferredSchedulingTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + namedType: io.k8s.api.core.v1.NodeSelector + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeCondition + map: + fields: + - name: lastHeartbeatTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.NodeConfigSource + map: + fields: + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapNodeConfigSource + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeConfigStatus + map: + fields: + - name: active + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + elementRelationship: granular + - name: assigned + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + elementRelationship: granular + - name: error + type: + scalar: string + - name: lastKnownGood + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeDaemonEndpoints + map: + fields: + - name: kubeletEndpoint + type: + namedType: io.k8s.api.core.v1.DaemonEndpoint + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Node + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.NodeSelector + map: + fields: + - name: nodeSelectorTerms + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSelectorRequirement + map: + fields: + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSelectorTerm + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorRequirement + elementRelationship: atomic + - name: matchFields + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSpec + map: + fields: + - name: configSource + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + elementRelationship: granular + - name: externalID + type: + scalar: string + - name: podCIDR + type: + scalar: string + - name: providerID + type: + scalar: string + - name: taints + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Taint + elementRelationship: atomic + - name: unschedulable + type: + scalar: boolean +- name: io.k8s.api.core.v1.NodeStatus + map: + fields: + - name: addresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeAddress + elementRelationship: associative + keys: + - type + - name: allocatable + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeCondition + elementRelationship: associative + keys: + - type + - name: config + type: + namedType: io.k8s.api.core.v1.NodeConfigStatus + elementRelationship: granular + - name: daemonEndpoints + type: + namedType: io.k8s.api.core.v1.NodeDaemonEndpoints + elementRelationship: granular + - name: images + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerImage + elementRelationship: atomic + - name: nodeInfo + type: + namedType: io.k8s.api.core.v1.NodeSystemInfo + elementRelationship: granular + - name: phase + type: + scalar: string + - name: volumesAttached + type: + list: + elementType: + namedType: io.k8s.api.core.v1.AttachedVolume + elementRelationship: atomic + - name: volumesInUse + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSystemInfo + map: + fields: + - name: architecture + type: + scalar: string + - name: bootID + type: + scalar: string + - name: containerRuntimeVersion + type: + scalar: string + - name: kernelVersion + type: + scalar: string + - name: kubeProxyVersion + type: + scalar: string + - name: kubeletVersion + type: + scalar: string + - name: machineID + type: + scalar: string + - name: operatingSystem + type: + scalar: string + - name: osImage + type: + scalar: string + - name: systemUUID + type: + scalar: string +- name: io.k8s.api.core.v1.ObjectFieldSelector + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldPath + type: + scalar: string +- name: io.k8s.api.core.v1.ObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldPath + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolume + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.PersistentVolumeSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.PersistentVolumeStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeClaim + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeClaimCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeClaimSpec + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: dataSource + type: + namedType: io.k8s.api.core.v1.TypedLocalObjectReference + elementRelationship: granular + - name: resources + type: + namedType: io.k8s.api.core.v1.ResourceRequirements + elementRelationship: granular + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: storageClassName + type: + scalar: string + - name: volumeMode + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimStatus + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimCondition + elementRelationship: associative + keys: + - type + - name: phase + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource + map: + fields: + - name: claimName + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.PersistentVolumeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolume + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeSpec + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: awsElasticBlockStore + type: + namedType: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + elementRelationship: granular + - name: azureDisk + type: + namedType: io.k8s.api.core.v1.AzureDiskVolumeSource + elementRelationship: granular + - name: azureFile + type: + namedType: io.k8s.api.core.v1.AzureFilePersistentVolumeSource + elementRelationship: granular + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: cephfs + type: + namedType: io.k8s.api.core.v1.CephFSPersistentVolumeSource + elementRelationship: granular + - name: cinder + type: + namedType: io.k8s.api.core.v1.CinderPersistentVolumeSource + elementRelationship: granular + - name: claimRef + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: csi + type: + namedType: io.k8s.api.core.v1.CSIPersistentVolumeSource + elementRelationship: granular + - name: fc + type: + namedType: io.k8s.api.core.v1.FCVolumeSource + elementRelationship: granular + - name: flexVolume + type: + namedType: io.k8s.api.core.v1.FlexPersistentVolumeSource + elementRelationship: granular + - name: flocker + type: + namedType: io.k8s.api.core.v1.FlockerVolumeSource + elementRelationship: granular + - name: gcePersistentDisk + type: + namedType: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + elementRelationship: granular + - name: glusterfs + type: + namedType: io.k8s.api.core.v1.GlusterfsVolumeSource + elementRelationship: granular + - name: hostPath + type: + namedType: io.k8s.api.core.v1.HostPathVolumeSource + elementRelationship: granular + - name: iscsi + type: + namedType: io.k8s.api.core.v1.ISCSIPersistentVolumeSource + elementRelationship: granular + - name: local + type: + namedType: io.k8s.api.core.v1.LocalVolumeSource + elementRelationship: granular + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nfs + type: + namedType: io.k8s.api.core.v1.NFSVolumeSource + elementRelationship: granular + - name: nodeAffinity + type: + namedType: io.k8s.api.core.v1.VolumeNodeAffinity + elementRelationship: granular + - name: persistentVolumeReclaimPolicy + type: + scalar: string + - name: photonPersistentDisk + type: + namedType: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + elementRelationship: granular + - name: portworxVolume + type: + namedType: io.k8s.api.core.v1.PortworxVolumeSource + elementRelationship: granular + - name: quobyte + type: + namedType: io.k8s.api.core.v1.QuobyteVolumeSource + elementRelationship: granular + - name: rbd + type: + namedType: io.k8s.api.core.v1.RBDPersistentVolumeSource + elementRelationship: granular + - name: scaleIO + type: + namedType: io.k8s.api.core.v1.ScaleIOPersistentVolumeSource + elementRelationship: granular + - name: storageClassName + type: + scalar: string + - name: storageos + type: + namedType: io.k8s.api.core.v1.StorageOSPersistentVolumeSource + elementRelationship: granular + - name: volumeMode + type: + scalar: string + - name: vsphereVolume + type: + namedType: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeStatus + map: + fields: + - name: message + type: + scalar: string + - name: phase + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: pdID + type: + scalar: string +- name: io.k8s.api.core.v1.Pod + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.PodSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.PodStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.PodAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.WeightedPodAffinityTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodAffinityTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodAffinityTerm + map: + fields: + - name: labelSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: namespaces + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: topologyKey + type: + scalar: string +- name: io.k8s.api.core.v1.PodAntiAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.WeightedPodAffinityTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodAffinityTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.PodDNSConfig + map: + fields: + - name: nameservers + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: options + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodDNSConfigOption + elementRelationship: atomic + - name: searches + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodDNSConfigOption + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.PodList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Pod + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.PodReadinessGate + map: + fields: + - name: conditionType + type: + scalar: string +- name: io.k8s.api.core.v1.PodSecurityContext + map: + fields: + - name: fsGroup + type: + scalar: numeric + - name: runAsGroup + type: + scalar: numeric + - name: runAsNonRoot + type: + scalar: boolean + - name: runAsUser + type: + scalar: numeric + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + elementRelationship: granular + - name: supplementalGroups + type: + list: + elementType: + scalar: numeric + elementRelationship: atomic + - name: sysctls + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Sysctl + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodSpec + map: + fields: + - name: activeDeadlineSeconds + type: + scalar: numeric + - name: affinity + type: + namedType: io.k8s.api.core.v1.Affinity + elementRelationship: granular + - name: automountServiceAccountToken + type: + scalar: boolean + - name: containers + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Container + elementRelationship: associative + keys: + - name + - name: dnsConfig + type: + namedType: io.k8s.api.core.v1.PodDNSConfig + elementRelationship: granular + - name: dnsPolicy + type: + scalar: string + - name: enableServiceLinks + type: + scalar: boolean + - name: hostAliases + type: + list: + elementType: + namedType: io.k8s.api.core.v1.HostAlias + elementRelationship: associative + keys: + - ip + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostname + type: + scalar: string + - name: imagePullSecrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: associative + keys: + - name + - name: initContainers + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Container + elementRelationship: associative + keys: + - name + - name: nodeName + type: + scalar: string + - name: nodeSelector + type: + map: + elementType: + scalar: string + - name: priority + type: + scalar: numeric + - name: priorityClassName + type: + scalar: string + - name: readinessGates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodReadinessGate + elementRelationship: atomic + - name: restartPolicy + type: + scalar: string + - name: runtimeClassName + type: + scalar: string + - name: schedulerName + type: + scalar: string + - name: securityContext + type: + namedType: io.k8s.api.core.v1.PodSecurityContext + elementRelationship: granular + - name: serviceAccount + type: + scalar: string + - name: serviceAccountName + type: + scalar: string + - name: shareProcessNamespace + type: + scalar: boolean + - name: subdomain + type: + scalar: string + - name: terminationGracePeriodSeconds + type: + scalar: numeric + - name: tolerations + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Toleration + elementRelationship: atomic + - name: volumes + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Volume + elementRelationship: associative + keys: + - name +- name: io.k8s.api.core.v1.PodStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodCondition + elementRelationship: associative + keys: + - type + - name: containerStatuses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerStatus + elementRelationship: atomic + - name: hostIP + type: + scalar: string + - name: initContainerStatuses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerStatus + elementRelationship: atomic + - name: message + type: + scalar: string + - name: nominatedNodeName + type: + scalar: string + - name: phase + type: + scalar: string + - name: podIP + type: + scalar: string + - name: qosClass + type: + scalar: string + - name: reason + type: + scalar: string + - name: startTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.PodTemplate + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.core.v1.PodTemplateList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodTemplate + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.PodTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.PodSpec + elementRelationship: granular +- name: io.k8s.api.core.v1.PortworxVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.PreferredSchedulingTerm + map: + fields: + - name: preference + type: + namedType: io.k8s.api.core.v1.NodeSelectorTerm + elementRelationship: granular + - name: weight + type: + scalar: numeric +- name: io.k8s.api.core.v1.Probe + map: + fields: + - name: exec + type: + namedType: io.k8s.api.core.v1.ExecAction + elementRelationship: granular + - name: failureThreshold + type: + scalar: numeric + - name: httpGet + type: + namedType: io.k8s.api.core.v1.HTTPGetAction + elementRelationship: granular + - name: initialDelaySeconds + type: + scalar: numeric + - name: periodSeconds + type: + scalar: numeric + - name: successThreshold + type: + scalar: numeric + - name: tcpSocket + type: + namedType: io.k8s.api.core.v1.TCPSocketAction + elementRelationship: granular + - name: timeoutSeconds + type: + scalar: numeric +- name: io.k8s.api.core.v1.ProjectedVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: sources + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeProjection + elementRelationship: atomic +- name: io.k8s.api.core.v1.QuobyteVolumeSource + map: + fields: + - name: group + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: registry + type: + scalar: string + - name: user + type: + scalar: string + - name: volume + type: + scalar: string +- name: io.k8s.api.core.v1.RBDPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: image + type: + scalar: string + - name: keyring + type: + scalar: string + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: pool + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.RBDVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: image + type: + scalar: string + - name: keyring + type: + scalar: string + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: pool + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.ReplicationController + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.ReplicationControllerSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.ReplicationControllerStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.ReplicationControllerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ReplicationControllerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ReplicationController + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ReplicationControllerSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.core.v1.ReplicationControllerStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ReplicationControllerCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.core.v1.ResourceFieldSelector + map: + fields: + - name: containerName + type: + scalar: string + - name: divisor + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + elementRelationship: granular + - name: resource + type: + scalar: string +- name: io.k8s.api.core.v1.ResourceQuota + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.ResourceQuotaSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.ResourceQuotaStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.ResourceQuotaList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ResourceQuota + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ResourceQuotaSpec + map: + fields: + - name: hard + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: scopeSelector + type: + namedType: io.k8s.api.core.v1.ScopeSelector + elementRelationship: granular + - name: scopes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.ResourceQuotaStatus + map: + fields: + - name: hard + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: used + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.core.v1.ResourceRequirements + map: + fields: + - name: limits + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: requests + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.core.v1.SELinuxOptions + map: + fields: + - name: level + type: + scalar: string + - name: role + type: + scalar: string + - name: type + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.ScaleIOPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: gateway + type: + scalar: string + - name: protectionDomain + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: sslEnabled + type: + scalar: boolean + - name: storageMode + type: + scalar: string + - name: storagePool + type: + scalar: string + - name: system + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.ScaleIOVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: gateway + type: + scalar: string + - name: protectionDomain + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: sslEnabled + type: + scalar: boolean + - name: storageMode + type: + scalar: string + - name: storagePool + type: + scalar: string + - name: system + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.ScopeSelector + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ScopedResourceSelectorRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.ScopedResourceSelectorRequirement + map: + fields: + - name: operator + type: + scalar: string + - name: scopeName + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.Secret + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + map: + elementType: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: stringData + type: + map: + elementType: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.SecretEnvSource + map: + fields: + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretKeySelector + map: + fields: + - name: key + type: + scalar: string + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Secret + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.SecretProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.core.v1.SecretVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: optional + type: + scalar: boolean + - name: secretName + type: + scalar: string +- name: io.k8s.api.core.v1.SecurityContext + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: capabilities + type: + namedType: io.k8s.api.core.v1.Capabilities + elementRelationship: granular + - name: privileged + type: + scalar: boolean + - name: procMount + type: + scalar: string + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: runAsGroup + type: + scalar: numeric + - name: runAsNonRoot + type: + scalar: boolean + - name: runAsUser + type: + scalar: numeric + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + elementRelationship: granular +- name: io.k8s.api.core.v1.Service + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.ServiceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.ServiceStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.ServiceAccount + map: + fields: + - name: apiVersion + type: + scalar: string + - name: automountServiceAccountToken + type: + scalar: boolean + - name: imagePullSecrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: secrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: associative + keys: + - name +- name: io.k8s.api.core.v1.ServiceAccountList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ServiceAccount + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ServiceAccountTokenProjection + map: + fields: + - name: audience + type: + scalar: string + - name: expirationSeconds + type: + scalar: numeric + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.ServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Service + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.ServicePort + map: + fields: + - name: name + type: + scalar: string + - name: nodePort + type: + scalar: numeric + - name: port + type: + scalar: numeric + - name: protocol + type: + scalar: string + - name: targetPort + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.core.v1.ServiceSpec + map: + fields: + - name: clusterIP + type: + scalar: string + - name: externalIPs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: externalName + type: + scalar: string + - name: externalTrafficPolicy + type: + scalar: string + - name: healthCheckNodePort + type: + scalar: numeric + - name: loadBalancerIP + type: + scalar: string + - name: loadBalancerSourceRanges + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ServicePort + elementRelationship: associative + keys: + - port + - name: publishNotReadyAddresses + type: + scalar: boolean + - name: selector + type: + map: + elementType: + scalar: string + - name: sessionAffinity + type: + scalar: string + - name: sessionAffinityConfig + type: + namedType: io.k8s.api.core.v1.SessionAffinityConfig + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ServiceStatus + map: + fields: + - name: loadBalancer + type: + namedType: io.k8s.api.core.v1.LoadBalancerStatus + elementRelationship: granular +- name: io.k8s.api.core.v1.SessionAffinityConfig + map: + fields: + - name: clientIP + type: + namedType: io.k8s.api.core.v1.ClientIPConfig + elementRelationship: granular +- name: io.k8s.api.core.v1.StorageOSPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: volumeName + type: + scalar: string + - name: volumeNamespace + type: + scalar: string +- name: io.k8s.api.core.v1.StorageOSVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: granular + - name: volumeName + type: + scalar: string + - name: volumeNamespace + type: + scalar: string +- name: io.k8s.api.core.v1.Sysctl + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.TCPSocketAction + map: + fields: + - name: host + type: + scalar: string + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.core.v1.Taint + map: + fields: + - name: effect + type: + scalar: string + - name: key + type: + scalar: string + - name: timeAdded + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.Toleration + map: + fields: + - name: effect + type: + scalar: string + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: tolerationSeconds + type: + scalar: numeric + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.TopologySelectorLabelRequirement + map: + fields: + - name: key + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.TopologySelectorTerm + map: + fields: + - name: matchLabelExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorLabelRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.TypedLocalObjectReference + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.Volume + map: + fields: + - name: awsElasticBlockStore + type: + namedType: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + elementRelationship: granular + - name: azureDisk + type: + namedType: io.k8s.api.core.v1.AzureDiskVolumeSource + elementRelationship: granular + - name: azureFile + type: + namedType: io.k8s.api.core.v1.AzureFileVolumeSource + elementRelationship: granular + - name: cephfs + type: + namedType: io.k8s.api.core.v1.CephFSVolumeSource + elementRelationship: granular + - name: cinder + type: + namedType: io.k8s.api.core.v1.CinderVolumeSource + elementRelationship: granular + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapVolumeSource + elementRelationship: granular + - name: downwardAPI + type: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeSource + elementRelationship: granular + - name: emptyDir + type: + namedType: io.k8s.api.core.v1.EmptyDirVolumeSource + elementRelationship: granular + - name: fc + type: + namedType: io.k8s.api.core.v1.FCVolumeSource + elementRelationship: granular + - name: flexVolume + type: + namedType: io.k8s.api.core.v1.FlexVolumeSource + elementRelationship: granular + - name: flocker + type: + namedType: io.k8s.api.core.v1.FlockerVolumeSource + elementRelationship: granular + - name: gcePersistentDisk + type: + namedType: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + elementRelationship: granular + - name: gitRepo + type: + namedType: io.k8s.api.core.v1.GitRepoVolumeSource + elementRelationship: granular + - name: glusterfs + type: + namedType: io.k8s.api.core.v1.GlusterfsVolumeSource + elementRelationship: granular + - name: hostPath + type: + namedType: io.k8s.api.core.v1.HostPathVolumeSource + elementRelationship: granular + - name: iscsi + type: + namedType: io.k8s.api.core.v1.ISCSIVolumeSource + elementRelationship: granular + - name: name + type: + scalar: string + - name: nfs + type: + namedType: io.k8s.api.core.v1.NFSVolumeSource + elementRelationship: granular + - name: persistentVolumeClaim + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource + elementRelationship: granular + - name: photonPersistentDisk + type: + namedType: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + elementRelationship: granular + - name: portworxVolume + type: + namedType: io.k8s.api.core.v1.PortworxVolumeSource + elementRelationship: granular + - name: projected + type: + namedType: io.k8s.api.core.v1.ProjectedVolumeSource + elementRelationship: granular + - name: quobyte + type: + namedType: io.k8s.api.core.v1.QuobyteVolumeSource + elementRelationship: granular + - name: rbd + type: + namedType: io.k8s.api.core.v1.RBDVolumeSource + elementRelationship: granular + - name: scaleIO + type: + namedType: io.k8s.api.core.v1.ScaleIOVolumeSource + elementRelationship: granular + - name: secret + type: + namedType: io.k8s.api.core.v1.SecretVolumeSource + elementRelationship: granular + - name: storageos + type: + namedType: io.k8s.api.core.v1.StorageOSVolumeSource + elementRelationship: granular + - name: vsphereVolume + type: + namedType: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + elementRelationship: granular + unions: + - fields: + - fieldName: awsElasticBlockStore + discriminatorValue: AWSElasticBlockStore + - fieldName: azureDisk + discriminatorValue: AzureDisk + - fieldName: azureFile + discriminatorValue: AzureFile + - fieldName: cephfs + discriminatorValue: CephFS + - fieldName: cinder + discriminatorValue: Cinder + - fieldName: configMap + discriminatorValue: ConfigMap + - fieldName: downwardAPI + discriminatorValue: DownwardAPI + - fieldName: emptyDir + discriminatorValue: EmptyDir + - fieldName: fc + discriminatorValue: FC + - fieldName: flexVolume + discriminatorValue: FlexVolume + - fieldName: flocker + discriminatorValue: Flocker + - fieldName: gcePersistentDisk + discriminatorValue: GCEPersistentDisk + - fieldName: gitRepo + discriminatorValue: GitRepo + - fieldName: glusterfs + discriminatorValue: Glusterfs + - fieldName: hostPath + discriminatorValue: HostPath + - fieldName: iscsi + discriminatorValue: ISCSI + - fieldName: nfs + discriminatorValue: NFS + - fieldName: persistentVolumeClaim + discriminatorValue: PersistentVolumeClaim + - fieldName: photonPersistentDisk + discriminatorValue: PhotonPersistentDisk + - fieldName: portworxVolume + discriminatorValue: PortworxVolume + - fieldName: projected + discriminatorValue: Projected + - fieldName: quobyte + discriminatorValue: Quobyte + - fieldName: rbd + discriminatorValue: RBD + - fieldName: scaleIO + discriminatorValue: ScaleIO + - fieldName: secret + discriminatorValue: Secret + - fieldName: storageos + discriminatorValue: StorageOS + - fieldName: vsphereVolume + discriminatorValue: VsphereVolume +- name: io.k8s.api.core.v1.VolumeDevice + map: + fields: + - name: devicePath + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.VolumeMount + map: + fields: + - name: mountPath + type: + scalar: string + - name: mountPropagation + type: + scalar: string + - name: name + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: subPath + type: + scalar: string +- name: io.k8s.api.core.v1.VolumeNodeAffinity + map: + fields: + - name: required + type: + namedType: io.k8s.api.core.v1.NodeSelector + elementRelationship: granular +- name: io.k8s.api.core.v1.VolumeProjection + map: + fields: + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapProjection + elementRelationship: granular + - name: downwardAPI + type: + namedType: io.k8s.api.core.v1.DownwardAPIProjection + elementRelationship: granular + - name: secret + type: + namedType: io.k8s.api.core.v1.SecretProjection + elementRelationship: granular + - name: serviceAccountToken + type: + namedType: io.k8s.api.core.v1.ServiceAccountTokenProjection + elementRelationship: granular +- name: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: storagePolicyID + type: + scalar: string + - name: storagePolicyName + type: + scalar: string + - name: volumePath + type: + scalar: string +- name: io.k8s.api.core.v1.WeightedPodAffinityTerm + map: + fields: + - name: podAffinityTerm + type: + namedType: io.k8s.api.core.v1.PodAffinityTerm + elementRelationship: granular + - name: weight + type: + scalar: numeric +- name: io.k8s.api.events.v1beta1.Event + map: + fields: + - name: action + type: + scalar: string + - name: apiVersion + type: + scalar: string + - name: deprecatedCount + type: + scalar: numeric + - name: deprecatedFirstTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deprecatedLastTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deprecatedSource + type: + namedType: io.k8s.api.core.v1.EventSource + elementRelationship: granular + - name: eventTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: note + type: + scalar: string + - name: reason + type: + scalar: string + - name: regarding + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: related + type: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: granular + - name: reportingController + type: + scalar: string + - name: reportingInstance + type: + scalar: string + - name: series + type: + namedType: io.k8s.api.events.v1beta1.EventSeries + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.events.v1beta1.EventList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.events.v1beta1.Event + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.events.v1beta1.EventSeries + map: + fields: + - name: count + type: + scalar: numeric + - name: lastObservedTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: state + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.AllowedFlexVolume + map: + fields: + - name: driver + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.AllowedHostPath + map: + fields: + - name: pathPrefix + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.extensions.v1beta1.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: templateGeneration + type: + scalar: numeric + - name: updateStrategy + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DeploymentRollback + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: rollbackTo + type: + namedType: io.k8s.api.extensions.v1beta1.RollbackConfig + elementRelationship: granular + - name: updatedAnnotations + type: + map: + elementType: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: rollbackTo + type: + namedType: io.k8s.api.extensions.v1beta1.RollbackConfig + elementRelationship: granular + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: strategy + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentStrategy + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.extensions.v1beta1.RollingUpdateDeployment + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.HTTPIngressPath + map: + fields: + - name: backend + type: + namedType: io.k8s.api.extensions.v1beta1.IngressBackend + elementRelationship: granular + - name: path + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue + map: + fields: + - name: paths + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.HTTPIngressPath + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.HostPortRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.IDRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.IPBlock + map: + fields: + - name: cidr + type: + scalar: string + - name: except + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.Ingress + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.IngressSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.IngressStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.IngressBackend + map: + fields: + - name: serviceName + type: + scalar: string + - name: servicePort + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.IngressList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.Ingress + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.IngressRule + map: + fields: + - name: host + type: + scalar: string + - name: http + type: + namedType: io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.IngressSpec + map: + fields: + - name: backend + type: + namedType: io.k8s.api.extensions.v1beta1.IngressBackend + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IngressRule + elementRelationship: atomic + - name: tls + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IngressTLS + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.IngressStatus + map: + fields: + - name: loadBalancer + type: + namedType: io.k8s.api.core.v1.LoadBalancerStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.IngressTLS + map: + fields: + - name: hosts + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: secretName + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.NetworkPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicySpec + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule + map: + fields: + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + elementRelationship: atomic + - name: to + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule + map: + fields: + - name: from + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + map: + fields: + - name: ipBlock + type: + namedType: io.k8s.api.extensions.v1beta1.IPBlock + elementRelationship: granular + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + map: + fields: + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: protocol + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.NetworkPolicySpec + map: + fields: + - name: egress + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule + elementRelationship: atomic + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule + elementRelationship: atomic + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: policyTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.PodSecurityPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: allowedCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedFlexVolumes + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.AllowedFlexVolume + elementRelationship: atomic + - name: allowedHostPaths + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.AllowedHostPath + elementRelationship: atomic + - name: allowedProcMountTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedUnsafeSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAddCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAllowPrivilegeEscalation + type: + scalar: boolean + - name: forbiddenSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: fsGroup + type: + namedType: io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions + elementRelationship: granular + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostPorts + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.HostPortRange + elementRelationship: atomic + - name: privileged + type: + scalar: boolean + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: requiredDropCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: runAsGroup + type: + namedType: io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions + elementRelationship: granular + - name: runAsUser + type: + namedType: io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions + elementRelationship: granular + - name: seLinux + type: + namedType: io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions + elementRelationship: granular + - name: supplementalGroups + type: + namedType: io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions + elementRelationship: granular + - name: volumes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.RollbackConfig + map: + fields: + - name: revision + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions + map: + fields: + - name: rule + type: + scalar: string + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.ScaleStatus + elementRelationship: granular +- name: io.k8s.api.extensions.v1beta1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.networking.v1.IPBlock + map: + fields: + - name: cidr + type: + scalar: string + - name: except + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.networking.v1.NetworkPolicySpec + elementRelationship: granular +- name: io.k8s.api.networking.v1.NetworkPolicyEgressRule + map: + fields: + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPort + elementRelationship: atomic + - name: to + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPeer + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicyIngressRule + map: + fields: + - name: from + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPeer + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPort + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.networking.v1.NetworkPolicyPeer + map: + fields: + - name: ipBlock + type: + namedType: io.k8s.api.networking.v1.IPBlock + elementRelationship: granular + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.networking.v1.NetworkPolicyPort + map: + fields: + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: protocol + type: + scalar: string +- name: io.k8s.api.networking.v1.NetworkPolicySpec + map: + fields: + - name: egress + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyEgressRule + elementRelationship: atomic + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyIngressRule + elementRelationship: atomic + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: policyTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.policy.v1beta1.AllowedFlexVolume + map: + fields: + - name: driver + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.AllowedHostPath + map: + fields: + - name: pathPrefix + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.policy.v1beta1.Eviction + map: + fields: + - name: apiVersion + type: + scalar: string + - name: deleteOptions + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions + elementRelationship: granular + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.FSGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.HostPortRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.IDRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudget + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudget + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: minAvailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus + map: + fields: + - name: currentHealthy + type: + scalar: numeric + - name: desiredHealthy + type: + scalar: numeric + - name: disruptedPods + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: disruptionsAllowed + type: + scalar: numeric + - name: expectedPods + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.policy.v1beta1.PodSecurityPolicySpec + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.PodSecurityPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicySpec + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: allowedCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedFlexVolumes + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.AllowedFlexVolume + elementRelationship: atomic + - name: allowedHostPaths + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.AllowedHostPath + elementRelationship: atomic + - name: allowedProcMountTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedUnsafeSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAddCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAllowPrivilegeEscalation + type: + scalar: boolean + - name: forbiddenSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: fsGroup + type: + namedType: io.k8s.api.policy.v1beta1.FSGroupStrategyOptions + elementRelationship: granular + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostPorts + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.HostPortRange + elementRelationship: atomic + - name: privileged + type: + scalar: boolean + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: requiredDropCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: runAsGroup + type: + namedType: io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions + elementRelationship: granular + - name: runAsUser + type: + namedType: io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions + elementRelationship: granular + - name: seLinux + type: + namedType: io.k8s.api.policy.v1beta1.SELinuxStrategyOptions + elementRelationship: granular + - name: supplementalGroups + type: + namedType: io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions + elementRelationship: granular + - name: volumes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.SELinuxStrategyOptions + map: + fields: + - name: rule + type: + scalar: string + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.rbac.v1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1.AggregationRule + elementRelationship: granular + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1.Subject + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.rbac.v1alpha1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1alpha1.AggregationRule + elementRelationship: granular + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1alpha1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1alpha1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1alpha1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1alpha1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1alpha1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1alpha1.Subject + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.rbac.v1beta1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1beta1.AggregationRule + elementRelationship: granular + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1beta1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1beta1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1beta1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1beta1.RoleRef + elementRelationship: granular + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1beta1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1beta1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1beta1.Subject + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.scheduling.v1alpha1.PriorityClass + map: + fields: + - name: apiVersion + type: + scalar: string + - name: description + type: + scalar: string + - name: globalDefault + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: value + type: + scalar: numeric +- name: io.k8s.api.scheduling.v1alpha1.PriorityClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.scheduling.v1alpha1.PriorityClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.scheduling.v1beta1.PriorityClass + map: + fields: + - name: apiVersion + type: + scalar: string + - name: description + type: + scalar: string + - name: globalDefault + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: value + type: + scalar: numeric +- name: io.k8s.api.scheduling.v1beta1.PriorityClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.scheduling.v1beta1.PriorityClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.settings.v1alpha1.PodPreset + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.settings.v1alpha1.PodPresetSpec + elementRelationship: granular +- name: io.k8s.api.settings.v1alpha1.PodPresetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.settings.v1alpha1.PodPreset + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.settings.v1alpha1.PodPresetSpec + map: + fields: + - name: env + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvVar + elementRelationship: atomic + - name: envFrom + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvFromSource + elementRelationship: atomic + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: volumeMounts + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeMount + elementRelationship: atomic + - name: volumes + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Volume + elementRelationship: atomic +- name: io.k8s.api.storage.v1.StorageClass + map: + fields: + - name: allowVolumeExpansion + type: + scalar: boolean + - name: allowedTopologies + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorTerm + elementRelationship: atomic + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: parameters + type: + map: + elementType: + scalar: string + - name: provisioner + type: + scalar: string + - name: reclaimPolicy + type: + scalar: string + - name: volumeBindingMode + type: + scalar: string +- name: io.k8s.api.storage.v1.StorageClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1.StorageClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeAttachment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentSource + map: + fields: + - name: persistentVolumeName + type: + scalar: string +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec + map: + fields: + - name: attacher + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: source + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentSource + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus + map: + fields: + - name: attachError + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeError + elementRelationship: granular + - name: attached + type: + scalar: boolean + - name: attachmentMetadata + type: + map: + elementType: + scalar: string + - name: detachError + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeError + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeError + map: + fields: + - name: message + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.storage.v1beta1.StorageClass + map: + fields: + - name: allowVolumeExpansion + type: + scalar: boolean + - name: allowedTopologies + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorTerm + elementRelationship: atomic + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: parameters + type: + map: + elementType: + scalar: string + - name: provisioner + type: + scalar: string + - name: reclaimPolicy + type: + scalar: string + - name: volumeBindingMode + type: + scalar: string +- name: io.k8s.api.storage.v1beta1.StorageClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1beta1.StorageClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.storage.v1beta1.VolumeAttachment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentStatus + elementRelationship: granular +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentSource + map: + fields: + - name: persistentVolumeName + type: + scalar: string +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentSpec + map: + fields: + - name: attacher + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: source + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentSource + elementRelationship: granular +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentStatus + map: + fields: + - name: attachError + type: + namedType: io.k8s.api.storage.v1beta1.VolumeError + elementRelationship: granular + - name: attached + type: + scalar: boolean + - name: attachmentMetadata + type: + map: + elementType: + scalar: string + - name: detachError + type: + namedType: io.k8s.api.storage.v1beta1.VolumeError + elementRelationship: granular +- name: io.k8s.api.storage.v1beta1.VolumeError + map: + fields: + - name: message + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition + map: + fields: + - name: JSONPath + type: + scalar: string + - name: description + type: + scalar: string + - name: format + type: + scalar: string + - name: name + type: + scalar: string + - name: priority + type: + scalar: numeric + - name: type + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus + elementRelationship: granular +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + map: + fields: + - name: categories + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: listKind + type: + scalar: string + - name: plural + type: + scalar: string + - name: shortNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: singular + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec + map: + fields: + - name: additionalPrinterColumns + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition + elementRelationship: atomic + - name: group + type: + scalar: string + - name: names + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + elementRelationship: granular + - name: scope + type: + scalar: string + - name: subresources + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources + elementRelationship: granular + - name: validation + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation + elementRelationship: granular + - name: version + type: + scalar: string + - name: versions + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus + map: + fields: + - name: acceptedNames + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + elementRelationship: granular + - name: conditions + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition + elementRelationship: atomic + - name: storedVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion + map: + fields: + - name: name + type: + scalar: string + - name: served + type: + scalar: boolean + - name: storage + type: + scalar: boolean +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale + map: + fields: + - name: labelSelectorPath + type: + scalar: string + - name: specReplicasPath + type: + scalar: string + - name: statusReplicasPath + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources + map: + fields: + - name: scale + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale + elementRelationship: granular + - name: status + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus + elementRelationship: granular +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation + map: + fields: + - name: openAPIV3Schema + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: granular +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation + map: + fields: + - name: description + type: + scalar: string + - name: url + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + map: + fields: + - name: $ref + type: + scalar: string + - name: $schema + type: + scalar: string + - name: additionalItems + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + elementRelationship: granular + - name: additionalProperties + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + elementRelationship: granular + - name: allOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: anyOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: default + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + elementRelationship: granular + - name: definitions + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: dependencies + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray + - name: description + type: + scalar: string + - name: enum + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + elementRelationship: atomic + - name: example + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + elementRelationship: granular + - name: exclusiveMaximum + type: + scalar: boolean + - name: exclusiveMinimum + type: + scalar: boolean + - name: externalDocs + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation + elementRelationship: granular + - name: format + type: + scalar: string + - name: id + type: + scalar: string + - name: items + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray + elementRelationship: granular + - name: maxItems + type: + scalar: numeric + - name: maxLength + type: + scalar: numeric + - name: maxProperties + type: + scalar: numeric + - name: maximum + type: + scalar: numeric + - name: minItems + type: + scalar: numeric + - name: minLength + type: + scalar: numeric + - name: minProperties + type: + scalar: numeric + - name: minimum + type: + scalar: numeric + - name: multipleOf + type: + scalar: numeric + - name: not + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: granular + - name: oneOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: pattern + type: + scalar: string + - name: patternProperties + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: properties + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: required + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: title + type: + scalar: string + - name: type + type: + scalar: string + - name: uniqueItems + type: + scalar: boolean +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.api.resource.Quantity + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: preferredVersion + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + elementRelationship: granular + - name: serverAddressByClientCIDRs + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + elementRelationship: atomic + - name: versions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroupList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: groups + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup + elementRelationship: atomic + - name: kind + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIResource + map: + fields: + - name: categories + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: group + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespaced + type: + scalar: boolean + - name: shortNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: singularName + type: + scalar: string + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: version + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: groupVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: resources + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.APIResource + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIVersions + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: serverAddressByClientCIDRs + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + elementRelationship: atomic + - name: versions + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions + map: + fields: + - name: apiVersion + type: + scalar: string + - name: dryRun + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: gracePeriodSeconds + type: + scalar: numeric + - name: kind + type: + scalar: string + - name: orphanDependents + type: + scalar: boolean + - name: preconditions + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions + elementRelationship: granular + - name: propagationPolicy + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + map: + fields: + - name: groupVersion + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Initializer + map: + fields: + - name: name + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Initializers + map: + fields: + - name: pending + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Initializer + elementRelationship: associative + keys: + - name + - name: result + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Status + elementRelationship: granular +- name: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement + elementRelationship: atomic + - name: matchLabels + type: + map: + elementType: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement + map: + fields: + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + map: + fields: + - name: continue + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: selfLink + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + scalar: untyped +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + map: + fields: + - name: annotations + type: + map: + elementType: + scalar: string + - name: clusterName + type: + scalar: string + - name: creationTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deletionGracePeriodSeconds + type: + scalar: numeric + - name: deletionTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: generateName + type: + scalar: string + - name: generation + type: + scalar: numeric + - name: initializers + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Initializers + elementRelationship: granular + - name: labels + type: + map: + elementType: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: ownerReferences + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + elementRelationship: associative + keys: + - uid + - name: resourceVersion + type: + scalar: string + - name: selfLink + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: blockOwnerDeletion + type: + scalar: boolean + - name: controller + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Patch + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions + map: + fields: + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + map: + fields: + - name: clientCIDR + type: + scalar: string + - name: serverAddress + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Status + map: + fields: + - name: apiVersion + type: + scalar: string + - name: code + type: + scalar: numeric + - name: details + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails + elementRelationship: granular + - name: kind + type: + scalar: string + - name: message + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular + - name: reason + type: + scalar: string + - name: status + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause + map: + fields: + - name: field + type: + scalar: string + - name: message + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails + map: + fields: + - name: causes + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause + elementRelationship: atomic + - name: group + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: retryAfterSeconds + type: + scalar: numeric + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Time + scalar: untyped +- name: io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent + map: + fields: + - name: object + type: + namedType: __untyped_atomic_ + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.apimachinery.pkg.runtime.RawExtension + map: + fields: + - name: Raw + type: + scalar: string +- name: io.k8s.apimachinery.pkg.util.intstr.IntOrString + scalar: untyped +- name: io.k8s.apimachinery.pkg.version.Info + map: + fields: + - name: buildDate + type: + scalar: string + - name: compiler + type: + scalar: string + - name: gitCommit + type: + scalar: string + - name: gitTreeState + type: + scalar: string + - name: gitVersion + type: + scalar: string + - name: goVersion + type: + scalar: string + - name: major + type: + scalar: string + - name: minor + type: + scalar: string + - name: platform + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus + elementRelationship: granular +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec + map: + fields: + - name: caBundle + type: + scalar: string + - name: group + type: + scalar: string + - name: groupPriorityMinimum + type: + scalar: numeric + - name: insecureSkipTLSVerify + type: + scalar: boolean + - name: service + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference + elementRelationship: granular + - name: version + type: + scalar: string + - name: versionPriority + type: + scalar: numeric +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition + elementRelationship: associative + keys: + - type +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus + elementRelationship: granular +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec + map: + fields: + - name: caBundle + type: + scalar: string + - name: group + type: + scalar: string + - name: groupPriorityMinimum + type: + scalar: numeric + - name: insecureSkipTLSVerify + type: + scalar: boolean + - name: service + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference + elementRelationship: granular + - name: version + type: + scalar: string + - name: versionPriority + type: + scalar: numeric +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition + elementRelationship: associative + keys: + - type +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic diff --git a/internal/testdata/k8s-schema-10pct-fieldoverride.yaml b/internal/testdata/k8s-schema-10pct-fieldoverride.yaml new file mode 100644 index 00000000..f3676c0e --- /dev/null +++ b/internal/testdata/k8s-schema-10pct-fieldoverride.yaml @@ -0,0 +1,10323 @@ +types: +- name: io.k8s.api.admissionregistration.v1alpha1.Initializer + map: + fields: + - name: name + type: + scalar: string + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.Rule + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1alpha1.InitializerConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: initializers + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.Initializer + elementRelationship: associative + keys: + - name + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta +- name: io.k8s.api.admissionregistration.v1alpha1.InitializerConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1alpha1.InitializerConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.admissionregistration.v1alpha1.Rule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: apiVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: webhooks + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.Webhook + elementRelationship: associative + keys: + - name +- name: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.admissionregistration.v1beta1.RuleWithOperations + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: apiVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: operations + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.admissionregistration.v1beta1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: webhooks + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.Webhook + elementRelationship: associative + keys: + - name +- name: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.admissionregistration.v1beta1.Webhook + map: + fields: + - name: clientConfig + type: + namedType: io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig + - name: failurePolicy + type: + scalar: string + - name: name + type: + scalar: string + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.admissionregistration.v1beta1.RuleWithOperations + elementRelationship: atomic + - name: sideEffects + type: + scalar: string +- name: io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig + map: + fields: + - name: caBundle + type: + scalar: string + - name: service + type: + namedType: io.k8s.api.admissionregistration.v1beta1.ServiceReference + elementRelationship: granular + - name: url + type: + scalar: string +- name: io.k8s.api.apps.v1.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1.DaemonSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1.DaemonSetStatus +- name: io.k8s.api.apps.v1.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1.DaemonSetUpdateStrategy +- name: io.k8s.api.apps.v1.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.apps.v1.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateDaemonSet + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1.DeploymentSpec + - name: status + type: + namedType: io.k8s.api.apps.v1.DeploymentStatus +- name: io.k8s.api.apps.v1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: strategy + type: + namedType: io.k8s.api.apps.v1.DeploymentStrategy + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.apps.v1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateDeployment + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1.ReplicaSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1.ReplicaSetStatus +- name: io.k8s.api.apps.v1.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.apps.v1.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.apps.v1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.apps.v1.StatefulSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1.StatefulSetStatus +- name: io.k8s.api.apps.v1.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1.StatefulSetUpdateStrategy + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentStatus +- name: io.k8s.api.apps.v1beta1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta1.DeploymentRollback + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: rollbackTo + type: + namedType: io.k8s.api.apps.v1beta1.RollbackConfig + - name: updatedAnnotations + type: + map: + elementType: + scalar: string +- name: io.k8s.api.apps.v1beta1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: rollbackTo + type: + namedType: io.k8s.api.apps.v1beta1.RollbackConfig + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: strategy + type: + namedType: io.k8s.api.apps.v1beta1.DeploymentStrategy + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.apps.v1beta1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta1.RollingUpdateDeployment + - name: type + type: + scalar: string + unions: + - discriminator: type + fields: + - fieldName: rollingUpdate + discriminatorValue: RollingUpdate +- name: io.k8s.api.apps.v1beta1.RollbackConfig + map: + fields: + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.ScaleSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.ScaleStatus +- name: io.k8s.api.apps.v1beta1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetStatus +- name: io.k8s.api.apps.v1beta1.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta1.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta1.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1beta1.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta1.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta1.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta1.RollingUpdateStatefulSetStrategy + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ControllerRevision + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + namedType: __untyped_atomic_ + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: revision + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.ControllerRevisionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ControllerRevision + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta2.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetStatus +- name: io.k8s.api.apps.v1beta2.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.apps.v1beta2.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy +- name: io.k8s.api.apps.v1beta2.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentStatus +- name: io.k8s.api.apps.v1beta2.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta2.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: strategy + type: + namedType: io.k8s.api.apps.v1beta2.DeploymentStrategy + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.apps.v1beta2.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateDeployment + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetStatus +- name: io.k8s.api.apps.v1beta2.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta2.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.apps.v1beta2.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.apps.v1beta2.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy + map: + fields: + - name: partition + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.ScaleStatus +- name: io.k8s.api.apps.v1beta2.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.StatefulSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetSpec + - name: status + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetStatus +- name: io.k8s.api.apps.v1beta2.StatefulSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.apps.v1beta2.StatefulSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.StatefulSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.apps.v1beta2.StatefulSetSpec + map: + fields: + - name: podManagementPolicy + type: + scalar: string + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: serviceName + type: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: updateStrategy + type: + namedType: io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy + elementRelationship: granular + - name: volumeClaimTemplates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic +- name: io.k8s.api.apps.v1beta2.StatefulSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.apps.v1beta2.StatefulSetCondition + elementRelationship: associative + keys: + - type + - name: currentReplicas + type: + scalar: numeric + - name: currentRevision + type: + scalar: string + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: updateRevision + type: + scalar: string + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.apps.v1beta2.StatefulSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.apps.v1beta2.RollingUpdateStatefulSetStrategy + - name: type + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.AuditSink + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec +- name: io.k8s.api.auditregistration.v1alpha1.AuditSinkList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.auditregistration.v1alpha1.AuditSink + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.auditregistration.v1alpha1.AuditSinkSpec + map: + fields: + - name: policy + type: + namedType: io.k8s.api.auditregistration.v1alpha1.Policy + - name: webhook + type: + namedType: io.k8s.api.auditregistration.v1alpha1.Webhook +- name: io.k8s.api.auditregistration.v1alpha1.Policy + map: + fields: + - name: level + type: + scalar: string + - name: stages + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.auditregistration.v1alpha1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.Webhook + map: + fields: + - name: clientConfig + type: + namedType: io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig + - name: throttle + type: + namedType: io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig +- name: io.k8s.api.auditregistration.v1alpha1.WebhookClientConfig + map: + fields: + - name: caBundle + type: + scalar: string + - name: service + type: + namedType: io.k8s.api.auditregistration.v1alpha1.ServiceReference + - name: url + type: + scalar: string +- name: io.k8s.api.auditregistration.v1alpha1.WebhookThrottleConfig + map: + fields: + - name: burst + type: + scalar: numeric + - name: qps + type: + scalar: numeric +- name: io.k8s.api.authentication.v1.TokenReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authentication.v1.TokenReviewSpec + - name: status + type: + namedType: io.k8s.api.authentication.v1.TokenReviewStatus +- name: io.k8s.api.authentication.v1.TokenReviewSpec + map: + fields: + - name: token + type: + scalar: string +- name: io.k8s.api.authentication.v1.TokenReviewStatus + map: + fields: + - name: authenticated + type: + scalar: boolean + - name: error + type: + scalar: string + - name: user + type: + namedType: io.k8s.api.authentication.v1.UserInfo +- name: io.k8s.api.authentication.v1.UserInfo + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: uid + type: + scalar: string + - name: username + type: + scalar: string +- name: io.k8s.api.authentication.v1beta1.TokenReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authentication.v1beta1.TokenReviewSpec + - name: status + type: + namedType: io.k8s.api.authentication.v1beta1.TokenReviewStatus +- name: io.k8s.api.authentication.v1beta1.TokenReviewSpec + map: + fields: + - name: token + type: + scalar: string +- name: io.k8s.api.authentication.v1beta1.TokenReviewStatus + map: + fields: + - name: authenticated + type: + scalar: boolean + - name: error + type: + scalar: string + - name: user + type: + namedType: io.k8s.api.authentication.v1beta1.UserInfo +- name: io.k8s.api.authentication.v1beta1.UserInfo + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: uid + type: + scalar: string + - name: username + type: + scalar: string +- name: io.k8s.api.authorization.v1.LocalSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1.NonResourceAttributes + map: + fields: + - name: path + type: + scalar: string + - name: verb + type: + scalar: string +- name: io.k8s.api.authorization.v1.NonResourceRule + map: + fields: + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1.ResourceAttributes + map: + fields: + - name: group + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resource + type: + scalar: string + - name: subresource + type: + scalar: string + - name: verb + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.api.authorization.v1.ResourceRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1.SelfSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1.SelfSubjectAccessReviewSpec + map: + fields: + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1.NonResourceAttributes + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1.ResourceAttributes + elementRelationship: granular +- name: io.k8s.api.authorization.v1.SelfSubjectRulesReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectRulesReviewStatus +- name: io.k8s.api.authorization.v1.SelfSubjectRulesReviewSpec + map: + fields: + - name: namespace + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1.SubjectAccessReviewSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1.NonResourceAttributes + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1.ResourceAttributes + - name: uid + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectAccessReviewStatus + map: + fields: + - name: allowed + type: + scalar: boolean + - name: denied + type: + scalar: boolean + - name: evaluationError + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.authorization.v1.SubjectRulesReviewStatus + map: + fields: + - name: evaluationError + type: + scalar: string + - name: incomplete + type: + scalar: boolean + - name: nonResourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1.NonResourceRule + elementRelationship: atomic + - name: resourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1.ResourceRule + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.LocalSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1beta1.NonResourceAttributes + map: + fields: + - name: path + type: + scalar: string + - name: verb + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.NonResourceRule + map: + fields: + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.ResourceAttributes + map: + fields: + - name: group + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resource + type: + scalar: string + - name: subresource + type: + scalar: string + - name: verb + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.ResourceRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1beta1.SelfSubjectAccessReviewSpec + map: + fields: + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.NonResourceAttributes + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.ResourceAttributes +- name: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus +- name: io.k8s.api.authorization.v1beta1.SelfSubjectRulesReviewSpec + map: + fields: + - name: namespace + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReview + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReviewSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: group + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.NonResourceAttributes + - name: resourceAttributes + type: + namedType: io.k8s.api.authorization.v1beta1.ResourceAttributes + - name: uid + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectAccessReviewStatus + map: + fields: + - name: allowed + type: + scalar: boolean + - name: denied + type: + scalar: boolean + - name: evaluationError + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.authorization.v1beta1.SubjectRulesReviewStatus + map: + fields: + - name: evaluationError + type: + scalar: string + - name: incomplete + type: + scalar: boolean + - name: nonResourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1beta1.NonResourceRule + elementRelationship: atomic + - name: resourceRules + type: + list: + elementType: + namedType: io.k8s.api.authorization.v1beta1.ResourceRule + elementRelationship: atomic +- name: io.k8s.api.autoscaling.v1.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec + - name: status + type: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v1.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v1.CrossVersionObjectReference + - name: targetCPUUtilizationPercentage + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.HorizontalPodAutoscalerStatus + map: + fields: + - name: currentCPUUtilizationPercentage + type: + scalar: numeric + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.autoscaling.v1.ScaleSpec + - name: status + type: + namedType: io.k8s.api.autoscaling.v1.ScaleStatus + elementRelationship: granular +- name: io.k8s.api.autoscaling.v1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.ExternalMetricSource + map: + fields: + - name: metricName + type: + scalar: string + - name: metricSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: targetValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus + map: + fields: + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: currentValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: metricName + type: + scalar: string + - name: metricSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: metrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.MetricSpec + elementRelationship: atomic + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference +- name: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.HorizontalPodAutoscalerCondition + elementRelationship: atomic + - name: currentMetrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta1.MetricStatus + elementRelationship: atomic + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v2beta1.MetricSpec + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta1.ExternalMetricSource + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta1.ObjectMetricSource + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta1.PodsMetricSource + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta1.ResourceMetricSource + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.MetricStatus + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta1.ExternalMetricStatus + elementRelationship: granular + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta1.PodsMetricStatus + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta1.ObjectMetricSource + map: + fields: + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference + - name: targetValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta1.ObjectMetricStatus + map: + fields: + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: currentValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: granular + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta1.CrossVersionObjectReference +- name: io.k8s.api.autoscaling.v2beta1.PodsMetricSource + map: + fields: + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta1.PodsMetricStatus + map: + fields: + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: metricName + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.autoscaling.v2beta1.ResourceMetricSource + map: + fields: + - name: name + type: + scalar: string + - name: targetAverageUtilization + type: + scalar: numeric + - name: targetAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta1.ResourceMetricStatus + map: + fields: + - name: currentAverageUtilization + type: + scalar: numeric + - name: currentAverageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.ExternalMetricSource + map: + fields: + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget +- name: io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec + - name: status + type: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscaler + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerSpec + map: + fields: + - name: maxReplicas + type: + scalar: numeric + - name: metrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.MetricSpec + elementRelationship: atomic + - name: minReplicas + type: + scalar: numeric + - name: scaleTargetRef + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + elementRelationship: granular +- name: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.HorizontalPodAutoscalerCondition + elementRelationship: atomic + - name: currentMetrics + type: + list: + elementType: + namedType: io.k8s.api.autoscaling.v2beta2.MetricStatus + elementRelationship: atomic + - name: currentReplicas + type: + scalar: numeric + - name: desiredReplicas + type: + scalar: numeric + - name: lastScaleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + map: + fields: + - name: name + type: + scalar: string + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.autoscaling.v2beta2.MetricSpec + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta2.ExternalMetricSource + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta2.ObjectMetricSource + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta2.PodsMetricSource + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta2.ResourceMetricSource + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.MetricStatus + map: + fields: + - name: external + type: + namedType: io.k8s.api.autoscaling.v2beta2.ExternalMetricStatus + - name: object + type: + namedType: io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus + - name: pods + type: + namedType: io.k8s.api.autoscaling.v2beta2.PodsMetricStatus + - name: resource + type: + namedType: io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus + - name: type + type: + scalar: string +- name: io.k8s.api.autoscaling.v2beta2.MetricTarget + map: + fields: + - name: averageUtilization + type: + scalar: numeric + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: type + type: + scalar: string + - name: value + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + map: + fields: + - name: averageUtilization + type: + scalar: numeric + - name: averageValue + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: value + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.autoscaling.v2beta2.ObjectMetricSource + map: + fields: + - name: describedObject + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget +- name: io.k8s.api.autoscaling.v2beta2.ObjectMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + - name: describedObject + type: + namedType: io.k8s.api.autoscaling.v2beta2.CrossVersionObjectReference + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier +- name: io.k8s.api.autoscaling.v2beta2.PodsMetricSource + map: + fields: + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget +- name: io.k8s.api.autoscaling.v2beta2.PodsMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + - name: metric + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricIdentifier +- name: io.k8s.api.autoscaling.v2beta2.ResourceMetricSource + map: + fields: + - name: name + type: + scalar: string + - name: target + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricTarget +- name: io.k8s.api.autoscaling.v2beta2.ResourceMetricStatus + map: + fields: + - name: current + type: + namedType: io.k8s.api.autoscaling.v2beta2.MetricValueStatus + - name: name + type: + scalar: string +- name: io.k8s.api.batch.v1.Job + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec + - name: status + type: + namedType: io.k8s.api.batch.v1.JobStatus + elementRelationship: granular +- name: io.k8s.api.batch.v1.JobCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.batch.v1.JobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v1.Job + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.batch.v1.JobSpec + map: + fields: + - name: activeDeadlineSeconds + type: + scalar: numeric + - name: backoffLimit + type: + scalar: numeric + - name: completions + type: + scalar: numeric + - name: manualSelector + type: + scalar: boolean + - name: parallelism + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: ttlSecondsAfterFinished + type: + scalar: numeric +- name: io.k8s.api.batch.v1.JobStatus + map: + fields: + - name: active + type: + scalar: numeric + - name: completionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.batch.v1.JobCondition + elementRelationship: associative + keys: + - type + - name: failed + type: + scalar: numeric + - name: startTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: succeeded + type: + scalar: numeric +- name: io.k8s.api.batch.v1beta1.CronJob + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.batch.v1beta1.CronJobSpec + - name: status + type: + namedType: io.k8s.api.batch.v1beta1.CronJobStatus +- name: io.k8s.api.batch.v1beta1.CronJobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v1beta1.CronJob + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.batch.v1beta1.CronJobSpec + map: + fields: + - name: concurrencyPolicy + type: + scalar: string + - name: failedJobsHistoryLimit + type: + scalar: numeric + - name: jobTemplate + type: + namedType: io.k8s.api.batch.v1beta1.JobTemplateSpec + elementRelationship: granular + - name: schedule + type: + scalar: string + - name: startingDeadlineSeconds + type: + scalar: numeric + - name: successfulJobsHistoryLimit + type: + scalar: numeric + - name: suspend + type: + scalar: boolean +- name: io.k8s.api.batch.v1beta1.CronJobStatus + map: + fields: + - name: active + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: atomic + - name: lastScheduleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.batch.v1beta1.JobTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec +- name: io.k8s.api.batch.v2alpha1.CronJob + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.batch.v2alpha1.CronJobSpec + - name: status + type: + namedType: io.k8s.api.batch.v2alpha1.CronJobStatus +- name: io.k8s.api.batch.v2alpha1.CronJobList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.batch.v2alpha1.CronJob + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.batch.v2alpha1.CronJobSpec + map: + fields: + - name: concurrencyPolicy + type: + scalar: string + - name: failedJobsHistoryLimit + type: + scalar: numeric + - name: jobTemplate + type: + namedType: io.k8s.api.batch.v2alpha1.JobTemplateSpec + - name: schedule + type: + scalar: string + - name: startingDeadlineSeconds + type: + scalar: numeric + - name: successfulJobsHistoryLimit + type: + scalar: numeric + - name: suspend + type: + scalar: boolean +- name: io.k8s.api.batch.v2alpha1.CronJobStatus + map: + fields: + - name: active + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: atomic + - name: lastScheduleTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.batch.v2alpha1.JobTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.batch.v1.JobSpec +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequest + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec + - name: status + type: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition + map: + fields: + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequest + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestSpec + map: + fields: + - name: extra + type: + map: + elementType: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: groups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: request + type: + scalar: string + - name: uid + type: + scalar: string + - name: usages + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: username + type: + scalar: string +- name: io.k8s.api.certificates.v1beta1.CertificateSigningRequestStatus + map: + fields: + - name: certificate + type: + scalar: string + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.certificates.v1beta1.CertificateSigningRequestCondition + elementRelationship: atomic +- name: io.k8s.api.coordination.v1beta1.Lease + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.coordination.v1beta1.LeaseSpec +- name: io.k8s.api.coordination.v1beta1.LeaseList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.coordination.v1beta1.Lease + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.coordination.v1beta1.LeaseSpec + map: + fields: + - name: acquireTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + - name: holderIdentity + type: + scalar: string + - name: leaseDurationSeconds + type: + scalar: numeric + - name: leaseTransitions + type: + scalar: numeric + - name: renewTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime +- name: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: partition + type: + scalar: numeric + - name: readOnly + type: + scalar: boolean + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.Affinity + map: + fields: + - name: nodeAffinity + type: + namedType: io.k8s.api.core.v1.NodeAffinity + - name: podAffinity + type: + namedType: io.k8s.api.core.v1.PodAffinity + - name: podAntiAffinity + type: + namedType: io.k8s.api.core.v1.PodAntiAffinity +- name: io.k8s.api.core.v1.AttachedVolume + map: + fields: + - name: devicePath + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.AzureDiskVolumeSource + map: + fields: + - name: cachingMode + type: + scalar: string + - name: diskName + type: + scalar: string + - name: diskURI + type: + scalar: string + - name: fsType + type: + scalar: string + - name: kind + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.AzureFilePersistentVolumeSource + map: + fields: + - name: readOnly + type: + scalar: boolean + - name: secretName + type: + scalar: string + - name: secretNamespace + type: + scalar: string + - name: shareName + type: + scalar: string +- name: io.k8s.api.core.v1.AzureFileVolumeSource + map: + fields: + - name: readOnly + type: + scalar: boolean + - name: secretName + type: + scalar: string + - name: shareName + type: + scalar: string +- name: io.k8s.api.core.v1.Binding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: target + type: + namedType: io.k8s.api.core.v1.ObjectReference +- name: io.k8s.api.core.v1.CSIPersistentVolumeSource + map: + fields: + - name: controllerPublishSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: nodePublishSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: nodeStageSecretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: readOnly + type: + scalar: boolean + - name: volumeAttributes + type: + map: + elementType: + scalar: string + - name: volumeHandle + type: + scalar: string +- name: io.k8s.api.core.v1.Capabilities + map: + fields: + - name: add + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: drop + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.CephFSPersistentVolumeSource + map: + fields: + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretFile + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.CephFSVolumeSource + map: + fields: + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretFile + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.CinderPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + elementRelationship: granular + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.CinderVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.ClientIPConfig + map: + fields: + - name: timeoutSeconds + type: + scalar: numeric +- name: io.k8s.api.core.v1.ComponentCondition + map: + fields: + - name: error + type: + scalar: string + - name: message + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ComponentStatus + map: + fields: + - name: apiVersion + type: + scalar: string + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ComponentCondition + elementRelationship: associative + keys: + - type + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta +- name: io.k8s.api.core.v1.ComponentStatusList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ComponentStatus + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ConfigMap + map: + fields: + - name: apiVersion + type: + scalar: string + - name: binaryData + type: + map: + elementType: + scalar: string + - name: data + type: + map: + elementType: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta +- name: io.k8s.api.core.v1.ConfigMapEnvSource + map: + fields: + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapKeySelector + map: + fields: + - name: key + type: + scalar: string + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ConfigMap + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ConfigMapNodeConfigSource + map: + fields: + - name: kubeletConfigKey + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.api.core.v1.ConfigMapProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.ConfigMapVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.Container + map: + fields: + - name: args + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: command + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: env + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvVar + elementRelationship: associative + keys: + - name + - name: envFrom + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvFromSource + elementRelationship: atomic + - name: image + type: + scalar: string + - name: imagePullPolicy + type: + scalar: string + - name: lifecycle + type: + namedType: io.k8s.api.core.v1.Lifecycle + - name: livenessProbe + type: + namedType: io.k8s.api.core.v1.Probe + - name: name + type: + scalar: string + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerPort + elementRelationship: associative + keys: + - containerPort + - protocol + - name: readinessProbe + type: + namedType: io.k8s.api.core.v1.Probe + - name: resources + type: + namedType: io.k8s.api.core.v1.ResourceRequirements + elementRelationship: granular + - name: securityContext + type: + namedType: io.k8s.api.core.v1.SecurityContext + - name: stdin + type: + scalar: boolean + - name: stdinOnce + type: + scalar: boolean + - name: terminationMessagePath + type: + scalar: string + - name: terminationMessagePolicy + type: + scalar: string + - name: tty + type: + scalar: boolean + - name: volumeDevices + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeDevice + elementRelationship: associative + keys: + - devicePath + - name: volumeMounts + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeMount + elementRelationship: associative + keys: + - mountPath + - name: workingDir + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerImage + map: + fields: + - name: names + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: sizeBytes + type: + scalar: numeric +- name: io.k8s.api.core.v1.ContainerPort + map: + fields: + - name: containerPort + type: + scalar: numeric + - name: hostIP + type: + scalar: string + - name: hostPort + type: + scalar: numeric + - name: name + type: + scalar: string + - name: protocol + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerState + map: + fields: + - name: running + type: + namedType: io.k8s.api.core.v1.ContainerStateRunning + - name: terminated + type: + namedType: io.k8s.api.core.v1.ContainerStateTerminated + - name: waiting + type: + namedType: io.k8s.api.core.v1.ContainerStateWaiting +- name: io.k8s.api.core.v1.ContainerStateRunning + map: + fields: + - name: startedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.ContainerStateTerminated + map: + fields: + - name: containerID + type: + scalar: string + - name: exitCode + type: + scalar: numeric + - name: finishedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: signal + type: + scalar: numeric + - name: startedAt + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.ContainerStateWaiting + map: + fields: + - name: message + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.core.v1.ContainerStatus + map: + fields: + - name: containerID + type: + scalar: string + - name: image + type: + scalar: string + - name: imageID + type: + scalar: string + - name: lastState + type: + namedType: io.k8s.api.core.v1.ContainerState + elementRelationship: granular + - name: name + type: + scalar: string + - name: ready + type: + scalar: boolean + - name: restartCount + type: + scalar: numeric + - name: state + type: + namedType: io.k8s.api.core.v1.ContainerState +- name: io.k8s.api.core.v1.DaemonEndpoint + map: + fields: + - name: Port + type: + scalar: numeric +- name: io.k8s.api.core.v1.DownwardAPIProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeFile + elementRelationship: atomic +- name: io.k8s.api.core.v1.DownwardAPIVolumeFile + map: + fields: + - name: fieldRef + type: + namedType: io.k8s.api.core.v1.ObjectFieldSelector + - name: mode + type: + scalar: numeric + - name: path + type: + scalar: string + - name: resourceFieldRef + type: + namedType: io.k8s.api.core.v1.ResourceFieldSelector +- name: io.k8s.api.core.v1.DownwardAPIVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeFile + elementRelationship: atomic +- name: io.k8s.api.core.v1.EmptyDirVolumeSource + map: + fields: + - name: medium + type: + scalar: string + - name: sizeLimit + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.core.v1.EndpointAddress + map: + fields: + - name: hostname + type: + scalar: string + - name: ip + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: targetRef + type: + namedType: io.k8s.api.core.v1.ObjectReference +- name: io.k8s.api.core.v1.EndpointPort + map: + fields: + - name: name + type: + scalar: string + - name: port + type: + scalar: numeric + - name: protocol + type: + scalar: string +- name: io.k8s.api.core.v1.EndpointSubset + map: + fields: + - name: addresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointAddress + elementRelationship: atomic + - name: notReadyAddresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointAddress + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointPort + elementRelationship: atomic +- name: io.k8s.api.core.v1.Endpoints + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: subsets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EndpointSubset + elementRelationship: atomic +- name: io.k8s.api.core.v1.EndpointsList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Endpoints + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.core.v1.EnvFromSource + map: + fields: + - name: configMapRef + type: + namedType: io.k8s.api.core.v1.ConfigMapEnvSource + - name: prefix + type: + scalar: string + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretEnvSource +- name: io.k8s.api.core.v1.EnvVar + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string + - name: valueFrom + type: + namedType: io.k8s.api.core.v1.EnvVarSource +- name: io.k8s.api.core.v1.EnvVarSource + map: + fields: + - name: configMapKeyRef + type: + namedType: io.k8s.api.core.v1.ConfigMapKeySelector + - name: fieldRef + type: + namedType: io.k8s.api.core.v1.ObjectFieldSelector + - name: resourceFieldRef + type: + namedType: io.k8s.api.core.v1.ResourceFieldSelector + - name: secretKeyRef + type: + namedType: io.k8s.api.core.v1.SecretKeySelector +- name: io.k8s.api.core.v1.Event + map: + fields: + - name: action + type: + scalar: string + - name: apiVersion + type: + scalar: string + - name: count + type: + scalar: numeric + - name: eventTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + - name: firstTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: involvedObject + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: kind + type: + scalar: string + - name: lastTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: reason + type: + scalar: string + - name: related + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: reportingComponent + type: + scalar: string + - name: reportingInstance + type: + scalar: string + - name: series + type: + namedType: io.k8s.api.core.v1.EventSeries + - name: source + type: + namedType: io.k8s.api.core.v1.EventSource + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.EventList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Event + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.EventSeries + map: + fields: + - name: count + type: + scalar: numeric + - name: lastObservedTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + elementRelationship: granular + - name: state + type: + scalar: string +- name: io.k8s.api.core.v1.EventSource + map: + fields: + - name: component + type: + scalar: string + - name: host + type: + scalar: string +- name: io.k8s.api.core.v1.ExecAction + map: + fields: + - name: command + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.FCVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: readOnly + type: + scalar: boolean + - name: targetWWNs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: wwids + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.FlexPersistentVolumeSource + map: + fields: + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: options + type: + map: + elementType: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference +- name: io.k8s.api.core.v1.FlexVolumeSource + map: + fields: + - name: driver + type: + scalar: string + - name: fsType + type: + scalar: string + - name: options + type: + map: + elementType: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference +- name: io.k8s.api.core.v1.FlockerVolumeSource + map: + fields: + - name: datasetName + type: + scalar: string + - name: datasetUUID + type: + scalar: string +- name: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: partition + type: + scalar: numeric + - name: pdName + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.GitRepoVolumeSource + map: + fields: + - name: directory + type: + scalar: string + - name: repository + type: + scalar: string + - name: revision + type: + scalar: string +- name: io.k8s.api.core.v1.GlusterfsVolumeSource + map: + fields: + - name: endpoints + type: + scalar: string + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.HTTPGetAction + map: + fields: + - name: host + type: + scalar: string + - name: httpHeaders + type: + list: + elementType: + namedType: io.k8s.api.core.v1.HTTPHeader + elementRelationship: atomic + - name: path + type: + scalar: string + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: scheme + type: + scalar: string +- name: io.k8s.api.core.v1.HTTPHeader + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.Handler + map: + fields: + - name: exec + type: + namedType: io.k8s.api.core.v1.ExecAction + elementRelationship: granular + - name: httpGet + type: + namedType: io.k8s.api.core.v1.HTTPGetAction + - name: tcpSocket + type: + namedType: io.k8s.api.core.v1.TCPSocketAction +- name: io.k8s.api.core.v1.HostAlias + map: + fields: + - name: hostnames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: ip + type: + scalar: string +- name: io.k8s.api.core.v1.HostPathVolumeSource + map: + fields: + - name: path + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ISCSIPersistentVolumeSource + map: + fields: + - name: chapAuthDiscovery + type: + scalar: boolean + - name: chapAuthSession + type: + scalar: boolean + - name: fsType + type: + scalar: string + - name: initiatorName + type: + scalar: string + - name: iqn + type: + scalar: string + - name: iscsiInterface + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: portals + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: targetPortal + type: + scalar: string +- name: io.k8s.api.core.v1.ISCSIVolumeSource + map: + fields: + - name: chapAuthDiscovery + type: + scalar: boolean + - name: chapAuthSession + type: + scalar: boolean + - name: fsType + type: + scalar: string + - name: initiatorName + type: + scalar: string + - name: iqn + type: + scalar: string + - name: iscsiInterface + type: + scalar: string + - name: lun + type: + scalar: numeric + - name: portals + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: targetPortal + type: + scalar: string +- name: io.k8s.api.core.v1.KeyToPath + map: + fields: + - name: key + type: + scalar: string + - name: mode + type: + scalar: numeric + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.Lifecycle + map: + fields: + - name: postStart + type: + namedType: io.k8s.api.core.v1.Handler + - name: preStop + type: + namedType: io.k8s.api.core.v1.Handler +- name: io.k8s.api.core.v1.LimitRange + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.LimitRangeSpec +- name: io.k8s.api.core.v1.LimitRangeItem + map: + fields: + - name: default + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: defaultRequest + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: max + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: maxLimitRequestRatio + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: min + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.LimitRangeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LimitRange + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.LimitRangeSpec + map: + fields: + - name: limits + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LimitRangeItem + elementRelationship: atomic +- name: io.k8s.api.core.v1.LoadBalancerIngress + map: + fields: + - name: hostname + type: + scalar: string + - name: ip + type: + scalar: string +- name: io.k8s.api.core.v1.LoadBalancerStatus + map: + fields: + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LoadBalancerIngress + elementRelationship: atomic +- name: io.k8s.api.core.v1.LocalObjectReference + map: + fields: + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.LocalVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.NFSVolumeSource + map: + fields: + - name: path + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: server + type: + scalar: string +- name: io.k8s.api.core.v1.Namespace + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.NamespaceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.core.v1.NamespaceStatus +- name: io.k8s.api.core.v1.NamespaceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Namespace + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.NamespaceSpec + map: + fields: + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NamespaceStatus + map: + fields: + - name: phase + type: + scalar: string +- name: io.k8s.api.core.v1.Node + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.NodeSpec + - name: status + type: + namedType: io.k8s.api.core.v1.NodeStatus +- name: io.k8s.api.core.v1.NodeAddress + map: + fields: + - name: address + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.NodeAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PreferredSchedulingTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + namedType: io.k8s.api.core.v1.NodeSelector +- name: io.k8s.api.core.v1.NodeCondition + map: + fields: + - name: lastHeartbeatTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.NodeConfigSource + map: + fields: + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapNodeConfigSource +- name: io.k8s.api.core.v1.NodeConfigStatus + map: + fields: + - name: active + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + elementRelationship: granular + - name: assigned + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + - name: error + type: + scalar: string + - name: lastKnownGood + type: + namedType: io.k8s.api.core.v1.NodeConfigSource +- name: io.k8s.api.core.v1.NodeDaemonEndpoints + map: + fields: + - name: kubeletEndpoint + type: + namedType: io.k8s.api.core.v1.DaemonEndpoint +- name: io.k8s.api.core.v1.NodeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Node + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.NodeSelector + map: + fields: + - name: nodeSelectorTerms + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSelectorRequirement + map: + fields: + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSelectorTerm + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorRequirement + elementRelationship: atomic + - name: matchFields + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeSelectorRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSpec + map: + fields: + - name: configSource + type: + namedType: io.k8s.api.core.v1.NodeConfigSource + - name: externalID + type: + scalar: string + - name: podCIDR + type: + scalar: string + - name: providerID + type: + scalar: string + - name: taints + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Taint + elementRelationship: atomic + - name: unschedulable + type: + scalar: boolean +- name: io.k8s.api.core.v1.NodeStatus + map: + fields: + - name: addresses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeAddress + elementRelationship: associative + keys: + - type + - name: allocatable + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.NodeCondition + elementRelationship: associative + keys: + - type + - name: config + type: + namedType: io.k8s.api.core.v1.NodeConfigStatus + - name: daemonEndpoints + type: + namedType: io.k8s.api.core.v1.NodeDaemonEndpoints + - name: images + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerImage + elementRelationship: atomic + - name: nodeInfo + type: + namedType: io.k8s.api.core.v1.NodeSystemInfo + - name: phase + type: + scalar: string + - name: volumesAttached + type: + list: + elementType: + namedType: io.k8s.api.core.v1.AttachedVolume + elementRelationship: atomic + - name: volumesInUse + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.NodeSystemInfo + map: + fields: + - name: architecture + type: + scalar: string + - name: bootID + type: + scalar: string + - name: containerRuntimeVersion + type: + scalar: string + - name: kernelVersion + type: + scalar: string + - name: kubeProxyVersion + type: + scalar: string + - name: kubeletVersion + type: + scalar: string + - name: machineID + type: + scalar: string + - name: operatingSystem + type: + scalar: string + - name: osImage + type: + scalar: string + - name: systemUUID + type: + scalar: string +- name: io.k8s.api.core.v1.ObjectFieldSelector + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldPath + type: + scalar: string +- name: io.k8s.api.core.v1.ObjectReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: fieldPath + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolume + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.PersistentVolumeSpec + - name: status + type: + namedType: io.k8s.api.core.v1.PersistentVolumeStatus +- name: io.k8s.api.core.v1.PersistentVolumeClaim + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimSpec + - name: status + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimStatus +- name: io.k8s.api.core.v1.PersistentVolumeClaimCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaim + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.PersistentVolumeClaimSpec + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: dataSource + type: + namedType: io.k8s.api.core.v1.TypedLocalObjectReference + - name: resources + type: + namedType: io.k8s.api.core.v1.ResourceRequirements + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: storageClassName + type: + scalar: string + - name: volumeMode + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimStatus + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimCondition + elementRelationship: associative + keys: + - type + - name: phase + type: + scalar: string +- name: io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource + map: + fields: + - name: claimName + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.core.v1.PersistentVolumeList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PersistentVolume + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.PersistentVolumeSpec + map: + fields: + - name: accessModes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: awsElasticBlockStore + type: + namedType: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + - name: azureDisk + type: + namedType: io.k8s.api.core.v1.AzureDiskVolumeSource + - name: azureFile + type: + namedType: io.k8s.api.core.v1.AzureFilePersistentVolumeSource + - name: capacity + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: cephfs + type: + namedType: io.k8s.api.core.v1.CephFSPersistentVolumeSource + - name: cinder + type: + namedType: io.k8s.api.core.v1.CinderPersistentVolumeSource + - name: claimRef + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: csi + type: + namedType: io.k8s.api.core.v1.CSIPersistentVolumeSource + - name: fc + type: + namedType: io.k8s.api.core.v1.FCVolumeSource + - name: flexVolume + type: + namedType: io.k8s.api.core.v1.FlexPersistentVolumeSource + elementRelationship: granular + - name: flocker + type: + namedType: io.k8s.api.core.v1.FlockerVolumeSource + - name: gcePersistentDisk + type: + namedType: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + - name: glusterfs + type: + namedType: io.k8s.api.core.v1.GlusterfsVolumeSource + - name: hostPath + type: + namedType: io.k8s.api.core.v1.HostPathVolumeSource + - name: iscsi + type: + namedType: io.k8s.api.core.v1.ISCSIPersistentVolumeSource + - name: local + type: + namedType: io.k8s.api.core.v1.LocalVolumeSource + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nfs + type: + namedType: io.k8s.api.core.v1.NFSVolumeSource + - name: nodeAffinity + type: + namedType: io.k8s.api.core.v1.VolumeNodeAffinity + elementRelationship: granular + - name: persistentVolumeReclaimPolicy + type: + scalar: string + - name: photonPersistentDisk + type: + namedType: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + - name: portworxVolume + type: + namedType: io.k8s.api.core.v1.PortworxVolumeSource + - name: quobyte + type: + namedType: io.k8s.api.core.v1.QuobyteVolumeSource + - name: rbd + type: + namedType: io.k8s.api.core.v1.RBDPersistentVolumeSource + - name: scaleIO + type: + namedType: io.k8s.api.core.v1.ScaleIOPersistentVolumeSource + - name: storageClassName + type: + scalar: string + - name: storageos + type: + namedType: io.k8s.api.core.v1.StorageOSPersistentVolumeSource + - name: volumeMode + type: + scalar: string + - name: vsphereVolume + type: + namedType: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + elementRelationship: granular +- name: io.k8s.api.core.v1.PersistentVolumeStatus + map: + fields: + - name: message + type: + scalar: string + - name: phase + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: pdID + type: + scalar: string +- name: io.k8s.api.core.v1.Pod + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.PodSpec + - name: status + type: + namedType: io.k8s.api.core.v1.PodStatus +- name: io.k8s.api.core.v1.PodAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.WeightedPodAffinityTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodAffinityTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodAffinityTerm + map: + fields: + - name: labelSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: namespaces + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: topologyKey + type: + scalar: string +- name: io.k8s.api.core.v1.PodAntiAffinity + map: + fields: + - name: preferredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.WeightedPodAffinityTerm + elementRelationship: atomic + - name: requiredDuringSchedulingIgnoredDuringExecution + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodAffinityTerm + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodCondition + map: + fields: + - name: lastProbeTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.PodDNSConfig + map: + fields: + - name: nameservers + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: options + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodDNSConfigOption + elementRelationship: atomic + - name: searches + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodDNSConfigOption + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.PodList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Pod + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.PodReadinessGate + map: + fields: + - name: conditionType + type: + scalar: string +- name: io.k8s.api.core.v1.PodSecurityContext + map: + fields: + - name: fsGroup + type: + scalar: numeric + - name: runAsGroup + type: + scalar: numeric + - name: runAsNonRoot + type: + scalar: boolean + - name: runAsUser + type: + scalar: numeric + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + - name: supplementalGroups + type: + list: + elementType: + scalar: numeric + elementRelationship: atomic + - name: sysctls + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Sysctl + elementRelationship: atomic +- name: io.k8s.api.core.v1.PodSpec + map: + fields: + - name: activeDeadlineSeconds + type: + scalar: numeric + - name: affinity + type: + namedType: io.k8s.api.core.v1.Affinity + elementRelationship: granular + - name: automountServiceAccountToken + type: + scalar: boolean + - name: containers + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Container + elementRelationship: associative + keys: + - name + - name: dnsConfig + type: + namedType: io.k8s.api.core.v1.PodDNSConfig + - name: dnsPolicy + type: + scalar: string + - name: enableServiceLinks + type: + scalar: boolean + - name: hostAliases + type: + list: + elementType: + namedType: io.k8s.api.core.v1.HostAlias + elementRelationship: associative + keys: + - ip + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostname + type: + scalar: string + - name: imagePullSecrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: associative + keys: + - name + - name: initContainers + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Container + elementRelationship: associative + keys: + - name + - name: nodeName + type: + scalar: string + - name: nodeSelector + type: + map: + elementType: + scalar: string + - name: priority + type: + scalar: numeric + - name: priorityClassName + type: + scalar: string + - name: readinessGates + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodReadinessGate + elementRelationship: atomic + - name: restartPolicy + type: + scalar: string + - name: runtimeClassName + type: + scalar: string + - name: schedulerName + type: + scalar: string + - name: securityContext + type: + namedType: io.k8s.api.core.v1.PodSecurityContext + - name: serviceAccount + type: + scalar: string + - name: serviceAccountName + type: + scalar: string + - name: shareProcessNamespace + type: + scalar: boolean + - name: subdomain + type: + scalar: string + - name: terminationGracePeriodSeconds + type: + scalar: numeric + - name: tolerations + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Toleration + elementRelationship: atomic + - name: volumes + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Volume + elementRelationship: associative + keys: + - name +- name: io.k8s.api.core.v1.PodStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodCondition + elementRelationship: associative + keys: + - type + - name: containerStatuses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerStatus + elementRelationship: atomic + - name: hostIP + type: + scalar: string + - name: initContainerStatuses + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ContainerStatus + elementRelationship: atomic + - name: message + type: + scalar: string + - name: nominatedNodeName + type: + scalar: string + - name: phase + type: + scalar: string + - name: podIP + type: + scalar: string + - name: qosClass + type: + scalar: string + - name: reason + type: + scalar: string + - name: startTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.core.v1.PodTemplate + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.core.v1.PodTemplateList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.PodTemplate + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.PodTemplateSpec + map: + fields: + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.PodSpec +- name: io.k8s.api.core.v1.PortworxVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: volumeID + type: + scalar: string +- name: io.k8s.api.core.v1.PreferredSchedulingTerm + map: + fields: + - name: preference + type: + namedType: io.k8s.api.core.v1.NodeSelectorTerm + - name: weight + type: + scalar: numeric +- name: io.k8s.api.core.v1.Probe + map: + fields: + - name: exec + type: + namedType: io.k8s.api.core.v1.ExecAction + - name: failureThreshold + type: + scalar: numeric + - name: httpGet + type: + namedType: io.k8s.api.core.v1.HTTPGetAction + - name: initialDelaySeconds + type: + scalar: numeric + - name: periodSeconds + type: + scalar: numeric + - name: successThreshold + type: + scalar: numeric + - name: tcpSocket + type: + namedType: io.k8s.api.core.v1.TCPSocketAction + - name: timeoutSeconds + type: + scalar: numeric +- name: io.k8s.api.core.v1.ProjectedVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: sources + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeProjection + elementRelationship: atomic +- name: io.k8s.api.core.v1.QuobyteVolumeSource + map: + fields: + - name: group + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: registry + type: + scalar: string + - name: user + type: + scalar: string + - name: volume + type: + scalar: string +- name: io.k8s.api.core.v1.RBDPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: image + type: + scalar: string + - name: keyring + type: + scalar: string + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: pool + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.RBDVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: image + type: + scalar: string + - name: keyring + type: + scalar: string + - name: monitors + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: pool + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.ReplicationController + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.ReplicationControllerSpec + - name: status + type: + namedType: io.k8s.api.core.v1.ReplicationControllerStatus +- name: io.k8s.api.core.v1.ReplicationControllerCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ReplicationControllerList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ReplicationController + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ReplicationControllerSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + elementRelationship: granular +- name: io.k8s.api.core.v1.ReplicationControllerStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ReplicationControllerCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.core.v1.ResourceFieldSelector + map: + fields: + - name: containerName + type: + scalar: string + - name: divisor + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: resource + type: + scalar: string +- name: io.k8s.api.core.v1.ResourceQuota + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.core.v1.ResourceQuotaSpec + - name: status + type: + namedType: io.k8s.api.core.v1.ResourceQuotaStatus +- name: io.k8s.api.core.v1.ResourceQuotaList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ResourceQuota + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ResourceQuotaSpec + map: + fields: + - name: hard + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: scopeSelector + type: + namedType: io.k8s.api.core.v1.ScopeSelector + - name: scopes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.ResourceQuotaStatus + map: + fields: + - name: hard + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: used + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.core.v1.ResourceRequirements + map: + fields: + - name: limits + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity + - name: requests + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity +- name: io.k8s.api.core.v1.SELinuxOptions + map: + fields: + - name: level + type: + scalar: string + - name: role + type: + scalar: string + - name: type + type: + scalar: string + - name: user + type: + scalar: string +- name: io.k8s.api.core.v1.ScaleIOPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: gateway + type: + scalar: string + - name: protectionDomain + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.SecretReference + - name: sslEnabled + type: + scalar: boolean + - name: storageMode + type: + scalar: string + - name: storagePool + type: + scalar: string + - name: system + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.ScaleIOVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: gateway + type: + scalar: string + - name: protectionDomain + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: sslEnabled + type: + scalar: boolean + - name: storageMode + type: + scalar: string + - name: storagePool + type: + scalar: string + - name: system + type: + scalar: string + - name: volumeName + type: + scalar: string +- name: io.k8s.api.core.v1.ScopeSelector + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ScopedResourceSelectorRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.ScopedResourceSelectorRequirement + map: + fields: + - name: operator + type: + scalar: string + - name: scopeName + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.Secret + map: + fields: + - name: apiVersion + type: + scalar: string + - name: data + type: + map: + elementType: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: stringData + type: + map: + elementType: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.SecretEnvSource + map: + fields: + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretKeySelector + map: + fields: + - name: key + type: + scalar: string + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Secret + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.SecretProjection + map: + fields: + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: name + type: + scalar: string + - name: optional + type: + scalar: boolean +- name: io.k8s.api.core.v1.SecretReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.core.v1.SecretVolumeSource + map: + fields: + - name: defaultMode + type: + scalar: numeric + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.KeyToPath + elementRelationship: atomic + - name: optional + type: + scalar: boolean + - name: secretName + type: + scalar: string +- name: io.k8s.api.core.v1.SecurityContext + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: capabilities + type: + namedType: io.k8s.api.core.v1.Capabilities + - name: privileged + type: + scalar: boolean + - name: procMount + type: + scalar: string + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: runAsGroup + type: + scalar: numeric + - name: runAsNonRoot + type: + scalar: boolean + - name: runAsUser + type: + scalar: numeric + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions +- name: io.k8s.api.core.v1.Service + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.core.v1.ServiceSpec + - name: status + type: + namedType: io.k8s.api.core.v1.ServiceStatus +- name: io.k8s.api.core.v1.ServiceAccount + map: + fields: + - name: apiVersion + type: + scalar: string + - name: automountServiceAccountToken + type: + scalar: boolean + - name: imagePullSecrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.LocalObjectReference + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: secrets + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ObjectReference + elementRelationship: associative + keys: + - name +- name: io.k8s.api.core.v1.ServiceAccountList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ServiceAccount + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ServiceAccountTokenProjection + map: + fields: + - name: audience + type: + scalar: string + - name: expirationSeconds + type: + scalar: numeric + - name: path + type: + scalar: string +- name: io.k8s.api.core.v1.ServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Service + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.core.v1.ServicePort + map: + fields: + - name: name + type: + scalar: string + - name: nodePort + type: + scalar: numeric + - name: port + type: + scalar: numeric + - name: protocol + type: + scalar: string + - name: targetPort + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.core.v1.ServiceSpec + map: + fields: + - name: clusterIP + type: + scalar: string + - name: externalIPs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: externalName + type: + scalar: string + - name: externalTrafficPolicy + type: + scalar: string + - name: healthCheckNodePort + type: + scalar: numeric + - name: loadBalancerIP + type: + scalar: string + - name: loadBalancerSourceRanges + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.core.v1.ServicePort + elementRelationship: associative + keys: + - port + - name: publishNotReadyAddresses + type: + scalar: boolean + - name: selector + type: + map: + elementType: + scalar: string + - name: sessionAffinity + type: + scalar: string + - name: sessionAffinityConfig + type: + namedType: io.k8s.api.core.v1.SessionAffinityConfig + elementRelationship: granular + - name: type + type: + scalar: string +- name: io.k8s.api.core.v1.ServiceStatus + map: + fields: + - name: loadBalancer + type: + namedType: io.k8s.api.core.v1.LoadBalancerStatus +- name: io.k8s.api.core.v1.SessionAffinityConfig + map: + fields: + - name: clientIP + type: + namedType: io.k8s.api.core.v1.ClientIPConfig +- name: io.k8s.api.core.v1.StorageOSPersistentVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: volumeName + type: + scalar: string + - name: volumeNamespace + type: + scalar: string +- name: io.k8s.api.core.v1.StorageOSVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: secretRef + type: + namedType: io.k8s.api.core.v1.LocalObjectReference + - name: volumeName + type: + scalar: string + - name: volumeNamespace + type: + scalar: string +- name: io.k8s.api.core.v1.Sysctl + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.TCPSocketAction + map: + fields: + - name: host + type: + scalar: string + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular +- name: io.k8s.api.core.v1.Taint + map: + fields: + - name: effect + type: + scalar: string + - name: key + type: + scalar: string + - name: timeAdded + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.Toleration + map: + fields: + - name: effect + type: + scalar: string + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: tolerationSeconds + type: + scalar: numeric + - name: value + type: + scalar: string +- name: io.k8s.api.core.v1.TopologySelectorLabelRequirement + map: + fields: + - name: key + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.core.v1.TopologySelectorTerm + map: + fields: + - name: matchLabelExpressions + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorLabelRequirement + elementRelationship: atomic +- name: io.k8s.api.core.v1.TypedLocalObjectReference + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.Volume + map: + fields: + - name: awsElasticBlockStore + type: + namedType: io.k8s.api.core.v1.AWSElasticBlockStoreVolumeSource + - name: azureDisk + type: + namedType: io.k8s.api.core.v1.AzureDiskVolumeSource + - name: azureFile + type: + namedType: io.k8s.api.core.v1.AzureFileVolumeSource + - name: cephfs + type: + namedType: io.k8s.api.core.v1.CephFSVolumeSource + - name: cinder + type: + namedType: io.k8s.api.core.v1.CinderVolumeSource + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapVolumeSource + - name: downwardAPI + type: + namedType: io.k8s.api.core.v1.DownwardAPIVolumeSource + - name: emptyDir + type: + namedType: io.k8s.api.core.v1.EmptyDirVolumeSource + - name: fc + type: + namedType: io.k8s.api.core.v1.FCVolumeSource + - name: flexVolume + type: + namedType: io.k8s.api.core.v1.FlexVolumeSource + - name: flocker + type: + namedType: io.k8s.api.core.v1.FlockerVolumeSource + - name: gcePersistentDisk + type: + namedType: io.k8s.api.core.v1.GCEPersistentDiskVolumeSource + elementRelationship: granular + - name: gitRepo + type: + namedType: io.k8s.api.core.v1.GitRepoVolumeSource + - name: glusterfs + type: + namedType: io.k8s.api.core.v1.GlusterfsVolumeSource + - name: hostPath + type: + namedType: io.k8s.api.core.v1.HostPathVolumeSource + - name: iscsi + type: + namedType: io.k8s.api.core.v1.ISCSIVolumeSource + - name: name + type: + scalar: string + - name: nfs + type: + namedType: io.k8s.api.core.v1.NFSVolumeSource + - name: persistentVolumeClaim + type: + namedType: io.k8s.api.core.v1.PersistentVolumeClaimVolumeSource + - name: photonPersistentDisk + type: + namedType: io.k8s.api.core.v1.PhotonPersistentDiskVolumeSource + - name: portworxVolume + type: + namedType: io.k8s.api.core.v1.PortworxVolumeSource + - name: projected + type: + namedType: io.k8s.api.core.v1.ProjectedVolumeSource + elementRelationship: granular + - name: quobyte + type: + namedType: io.k8s.api.core.v1.QuobyteVolumeSource + - name: rbd + type: + namedType: io.k8s.api.core.v1.RBDVolumeSource + - name: scaleIO + type: + namedType: io.k8s.api.core.v1.ScaleIOVolumeSource + - name: secret + type: + namedType: io.k8s.api.core.v1.SecretVolumeSource + - name: storageos + type: + namedType: io.k8s.api.core.v1.StorageOSVolumeSource + elementRelationship: granular + - name: vsphereVolume + type: + namedType: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + unions: + - fields: + - fieldName: awsElasticBlockStore + discriminatorValue: AWSElasticBlockStore + - fieldName: azureDisk + discriminatorValue: AzureDisk + - fieldName: azureFile + discriminatorValue: AzureFile + - fieldName: cephfs + discriminatorValue: CephFS + - fieldName: cinder + discriminatorValue: Cinder + - fieldName: configMap + discriminatorValue: ConfigMap + - fieldName: downwardAPI + discriminatorValue: DownwardAPI + - fieldName: emptyDir + discriminatorValue: EmptyDir + - fieldName: fc + discriminatorValue: FC + - fieldName: flexVolume + discriminatorValue: FlexVolume + - fieldName: flocker + discriminatorValue: Flocker + - fieldName: gcePersistentDisk + discriminatorValue: GCEPersistentDisk + - fieldName: gitRepo + discriminatorValue: GitRepo + - fieldName: glusterfs + discriminatorValue: Glusterfs + - fieldName: hostPath + discriminatorValue: HostPath + - fieldName: iscsi + discriminatorValue: ISCSI + - fieldName: nfs + discriminatorValue: NFS + - fieldName: persistentVolumeClaim + discriminatorValue: PersistentVolumeClaim + - fieldName: photonPersistentDisk + discriminatorValue: PhotonPersistentDisk + - fieldName: portworxVolume + discriminatorValue: PortworxVolume + - fieldName: projected + discriminatorValue: Projected + - fieldName: quobyte + discriminatorValue: Quobyte + - fieldName: rbd + discriminatorValue: RBD + - fieldName: scaleIO + discriminatorValue: ScaleIO + - fieldName: secret + discriminatorValue: Secret + - fieldName: storageos + discriminatorValue: StorageOS + - fieldName: vsphereVolume + discriminatorValue: VsphereVolume +- name: io.k8s.api.core.v1.VolumeDevice + map: + fields: + - name: devicePath + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.core.v1.VolumeMount + map: + fields: + - name: mountPath + type: + scalar: string + - name: mountPropagation + type: + scalar: string + - name: name + type: + scalar: string + - name: readOnly + type: + scalar: boolean + - name: subPath + type: + scalar: string +- name: io.k8s.api.core.v1.VolumeNodeAffinity + map: + fields: + - name: required + type: + namedType: io.k8s.api.core.v1.NodeSelector +- name: io.k8s.api.core.v1.VolumeProjection + map: + fields: + - name: configMap + type: + namedType: io.k8s.api.core.v1.ConfigMapProjection + - name: downwardAPI + type: + namedType: io.k8s.api.core.v1.DownwardAPIProjection + - name: secret + type: + namedType: io.k8s.api.core.v1.SecretProjection + - name: serviceAccountToken + type: + namedType: io.k8s.api.core.v1.ServiceAccountTokenProjection +- name: io.k8s.api.core.v1.VsphereVirtualDiskVolumeSource + map: + fields: + - name: fsType + type: + scalar: string + - name: storagePolicyID + type: + scalar: string + - name: storagePolicyName + type: + scalar: string + - name: volumePath + type: + scalar: string +- name: io.k8s.api.core.v1.WeightedPodAffinityTerm + map: + fields: + - name: podAffinityTerm + type: + namedType: io.k8s.api.core.v1.PodAffinityTerm + - name: weight + type: + scalar: numeric +- name: io.k8s.api.events.v1beta1.Event + map: + fields: + - name: action + type: + scalar: string + - name: apiVersion + type: + scalar: string + - name: deprecatedCount + type: + scalar: numeric + - name: deprecatedFirstTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deprecatedLastTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deprecatedSource + type: + namedType: io.k8s.api.core.v1.EventSource + - name: eventTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: note + type: + scalar: string + - name: reason + type: + scalar: string + - name: regarding + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: related + type: + namedType: io.k8s.api.core.v1.ObjectReference + - name: reportingController + type: + scalar: string + - name: reportingInstance + type: + scalar: string + - name: series + type: + namedType: io.k8s.api.events.v1beta1.EventSeries + - name: type + type: + scalar: string +- name: io.k8s.api.events.v1beta1.EventList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.events.v1beta1.Event + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.events.v1beta1.EventSeries + map: + fields: + - name: count + type: + scalar: numeric + - name: lastObservedTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + - name: state + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.AllowedFlexVolume + map: + fields: + - name: driver + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.AllowedHostPath + map: + fields: + - name: pathPrefix + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.extensions.v1beta1.DaemonSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetSpec + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetStatus +- name: io.k8s.api.extensions.v1beta1.DaemonSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DaemonSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DaemonSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.DaemonSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec + - name: templateGeneration + type: + scalar: numeric + - name: updateStrategy + type: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy +- name: io.k8s.api.extensions.v1beta1.DaemonSetStatus + map: + fields: + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DaemonSetCondition + elementRelationship: associative + keys: + - type + - name: currentNumberScheduled + type: + scalar: numeric + - name: desiredNumberScheduled + type: + scalar: numeric + - name: numberAvailable + type: + scalar: numeric + - name: numberMisscheduled + type: + scalar: numeric + - name: numberReady + type: + scalar: numeric + - name: numberUnavailable + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: updatedNumberScheduled + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.DaemonSetUpdateStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.Deployment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentSpec + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentStatus +- name: io.k8s.api.extensions.v1beta1.DeploymentCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: lastUpdateTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DeploymentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.Deployment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.DeploymentRollback + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: rollbackTo + type: + namedType: io.k8s.api.extensions.v1beta1.RollbackConfig + elementRelationship: granular + - name: updatedAnnotations + type: + map: + elementType: + scalar: string +- name: io.k8s.api.extensions.v1beta1.DeploymentSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: paused + type: + scalar: boolean + - name: progressDeadlineSeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: revisionHistoryLimit + type: + scalar: numeric + - name: rollbackTo + type: + namedType: io.k8s.api.extensions.v1beta1.RollbackConfig + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: strategy + type: + namedType: io.k8s.api.extensions.v1beta1.DeploymentStrategy + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.extensions.v1beta1.DeploymentStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: collisionCount + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.DeploymentCondition + elementRelationship: associative + keys: + - type + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: unavailableReplicas + type: + scalar: numeric + - name: updatedReplicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.DeploymentStrategy + map: + fields: + - name: rollingUpdate + type: + namedType: io.k8s.api.extensions.v1beta1.RollingUpdateDeployment + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.HTTPIngressPath + map: + fields: + - name: backend + type: + namedType: io.k8s.api.extensions.v1beta1.IngressBackend + - name: path + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue + map: + fields: + - name: paths + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.HTTPIngressPath + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.HostPortRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.IDRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.IPBlock + map: + fields: + - name: cidr + type: + scalar: string + - name: except + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.Ingress + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.IngressSpec + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.IngressStatus +- name: io.k8s.api.extensions.v1beta1.IngressBackend + map: + fields: + - name: serviceName + type: + scalar: string + - name: servicePort + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.extensions.v1beta1.IngressList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.Ingress + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.IngressRule + map: + fields: + - name: host + type: + scalar: string + - name: http + type: + namedType: io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue +- name: io.k8s.api.extensions.v1beta1.IngressSpec + map: + fields: + - name: backend + type: + namedType: io.k8s.api.extensions.v1beta1.IngressBackend + elementRelationship: granular + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IngressRule + elementRelationship: atomic + - name: tls + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IngressTLS + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.IngressStatus + map: + fields: + - name: loadBalancer + type: + namedType: io.k8s.api.core.v1.LoadBalancerStatus +- name: io.k8s.api.extensions.v1beta1.IngressTLS + map: + fields: + - name: hosts + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: secretName + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.NetworkPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicySpec +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule + map: + fields: + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + elementRelationship: atomic + - name: to + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule + map: + fields: + - name: from + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyPeer + map: + fields: + - name: ipBlock + type: + namedType: io.k8s.api.extensions.v1beta1.IPBlock + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.extensions.v1beta1.NetworkPolicyPort + map: + fields: + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + elementRelationship: granular + - name: protocol + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.NetworkPolicySpec + map: + fields: + - name: egress + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyEgressRule + elementRelationship: atomic + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.NetworkPolicyIngressRule + elementRelationship: atomic + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: policyTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.PodSecurityPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.PodSecurityPolicySpec + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: allowedCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedFlexVolumes + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.AllowedFlexVolume + elementRelationship: atomic + - name: allowedHostPaths + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.AllowedHostPath + elementRelationship: atomic + - name: allowedProcMountTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedUnsafeSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAddCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAllowPrivilegeEscalation + type: + scalar: boolean + - name: forbiddenSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: fsGroup + type: + namedType: io.k8s.api.extensions.v1beta1.FSGroupStrategyOptions + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostPorts + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.HostPortRange + elementRelationship: atomic + - name: privileged + type: + scalar: boolean + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: requiredDropCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: runAsGroup + type: + namedType: io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions + - name: runAsUser + type: + namedType: io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions + - name: seLinux + type: + namedType: io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions + - name: supplementalGroups + type: + namedType: io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions + - name: volumes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.extensions.v1beta1.ReplicaSet + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetSpec + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetStatus +- name: io.k8s.api.extensions.v1beta1.ReplicaSetCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.ReplicaSetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSet + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.extensions.v1beta1.ReplicaSetSpec + map: + fields: + - name: minReadySeconds + type: + scalar: numeric + - name: replicas + type: + scalar: numeric + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: template + type: + namedType: io.k8s.api.core.v1.PodTemplateSpec +- name: io.k8s.api.extensions.v1beta1.ReplicaSetStatus + map: + fields: + - name: availableReplicas + type: + scalar: numeric + - name: conditions + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.ReplicaSetCondition + elementRelationship: associative + keys: + - type + - name: fullyLabeledReplicas + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric + - name: readyReplicas + type: + scalar: numeric + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.RollbackConfig + map: + fields: + - name: revision + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.RollingUpdateDaemonSet + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.extensions.v1beta1.RollingUpdateDeployment + map: + fields: + - name: maxSurge + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString +- name: io.k8s.api.extensions.v1beta1.RunAsGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.RunAsUserStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.SELinuxStrategyOptions + map: + fields: + - name: rule + type: + scalar: string + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions +- name: io.k8s.api.extensions.v1beta1.Scale + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.extensions.v1beta1.ScaleSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.api.extensions.v1beta1.ScaleStatus +- name: io.k8s.api.extensions.v1beta1.ScaleSpec + map: + fields: + - name: replicas + type: + scalar: numeric +- name: io.k8s.api.extensions.v1beta1.ScaleStatus + map: + fields: + - name: replicas + type: + scalar: numeric + - name: selector + type: + map: + elementType: + scalar: string + - name: targetSelector + type: + scalar: string +- name: io.k8s.api.extensions.v1beta1.SupplementalGroupsStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.extensions.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.networking.v1.IPBlock + map: + fields: + - name: cidr + type: + scalar: string + - name: except + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.networking.v1.NetworkPolicySpec +- name: io.k8s.api.networking.v1.NetworkPolicyEgressRule + map: + fields: + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPort + elementRelationship: atomic + - name: to + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPeer + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicyIngressRule + map: + fields: + - name: from + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPeer + elementRelationship: atomic + - name: ports + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyPort + elementRelationship: atomic +- name: io.k8s.api.networking.v1.NetworkPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.networking.v1.NetworkPolicyPeer + map: + fields: + - name: ipBlock + type: + namedType: io.k8s.api.networking.v1.IPBlock + - name: namespaceSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.networking.v1.NetworkPolicyPort + map: + fields: + - name: port + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: protocol + type: + scalar: string +- name: io.k8s.api.networking.v1.NetworkPolicySpec + map: + fields: + - name: egress + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyEgressRule + elementRelationship: atomic + - name: ingress + type: + list: + elementType: + namedType: io.k8s.api.networking.v1.NetworkPolicyIngressRule + elementRelationship: atomic + - name: podSelector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: policyTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.policy.v1beta1.AllowedFlexVolume + map: + fields: + - name: driver + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.AllowedHostPath + map: + fields: + - name: pathPrefix + type: + scalar: string + - name: readOnly + type: + scalar: boolean +- name: io.k8s.api.policy.v1beta1.Eviction + map: + fields: + - name: apiVersion + type: + scalar: string + - name: deleteOptions + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta +- name: io.k8s.api.policy.v1beta1.FSGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.HostPortRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.IDRange + map: + fields: + - name: max + type: + scalar: numeric + - name: min + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudget + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec + - name: status + type: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.PodDisruptionBudget + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetSpec + map: + fields: + - name: maxUnavailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: minAvailable + type: + namedType: io.k8s.apimachinery.pkg.util.intstr.IntOrString + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector +- name: io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus + map: + fields: + - name: currentHealthy + type: + scalar: numeric + - name: desiredHealthy + type: + scalar: numeric + - name: disruptedPods + type: + map: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: disruptionsAllowed + type: + scalar: numeric + - name: expectedPods + type: + scalar: numeric + - name: observedGeneration + type: + scalar: numeric +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicy + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.policy.v1beta1.PodSecurityPolicySpec +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicyList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.PodSecurityPolicy + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.policy.v1beta1.PodSecurityPolicySpec + map: + fields: + - name: allowPrivilegeEscalation + type: + scalar: boolean + - name: allowedCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedFlexVolumes + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.AllowedFlexVolume + elementRelationship: atomic + - name: allowedHostPaths + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.AllowedHostPath + elementRelationship: atomic + - name: allowedProcMountTypes + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: allowedUnsafeSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAddCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: defaultAllowPrivilegeEscalation + type: + scalar: boolean + - name: forbiddenSysctls + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: fsGroup + type: + namedType: io.k8s.api.policy.v1beta1.FSGroupStrategyOptions + - name: hostIPC + type: + scalar: boolean + - name: hostNetwork + type: + scalar: boolean + - name: hostPID + type: + scalar: boolean + - name: hostPorts + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.HostPortRange + elementRelationship: atomic + - name: privileged + type: + scalar: boolean + - name: readOnlyRootFilesystem + type: + scalar: boolean + - name: requiredDropCapabilities + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: runAsGroup + type: + namedType: io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions + - name: runAsUser + type: + namedType: io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions + - name: seLinux + type: + namedType: io.k8s.api.policy.v1beta1.SELinuxStrategyOptions + - name: supplementalGroups + type: + namedType: io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions + - name: volumes + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.policy.v1beta1.RunAsGroupStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.RunAsUserStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.policy.v1beta1.SELinuxStrategyOptions + map: + fields: + - name: rule + type: + scalar: string + - name: seLinuxOptions + type: + namedType: io.k8s.api.core.v1.SELinuxOptions + elementRelationship: granular +- name: io.k8s.api.policy.v1beta1.SupplementalGroupsStrategyOptions + map: + fields: + - name: ranges + type: + list: + elementType: + namedType: io.k8s.api.policy.v1beta1.IDRange + elementRelationship: atomic + - name: rule + type: + scalar: string +- name: io.k8s.api.rbac.v1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1.AggregationRule + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1.Subject + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.rbac.v1alpha1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1alpha1.AggregationRule + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1alpha1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.rbac.v1alpha1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1alpha1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1alpha1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1alpha1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1alpha1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1alpha1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1alpha1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1alpha1.Subject + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.rbac.v1beta1.AggregationRule + map: + fields: + - name: clusterRoleSelectors + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRole + map: + fields: + - name: aggregationRule + type: + namedType: io.k8s.api.rbac.v1beta1.AggregationRule + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1beta1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.ClusterRoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.ClusterRoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1beta1.ClusterRoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.ClusterRole + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1beta1.PolicyRule + map: + fields: + - name: apiGroups + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: nonResourceURLs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resourceNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: resources + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.Role + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: rules + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.PolicyRule + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.RoleBinding + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: roleRef + type: + namedType: io.k8s.api.rbac.v1beta1.RoleRef + - name: subjects + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Subject + elementRelationship: atomic +- name: io.k8s.api.rbac.v1beta1.RoleBindingList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.RoleBinding + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1beta1.RoleList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.rbac.v1beta1.Role + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.rbac.v1beta1.RoleRef + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string +- name: io.k8s.api.rbac.v1beta1.Subject + map: + fields: + - name: apiGroup + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.api.scheduling.v1alpha1.PriorityClass + map: + fields: + - name: apiVersion + type: + scalar: string + - name: description + type: + scalar: string + - name: globalDefault + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: value + type: + scalar: numeric +- name: io.k8s.api.scheduling.v1alpha1.PriorityClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.scheduling.v1alpha1.PriorityClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.scheduling.v1beta1.PriorityClass + map: + fields: + - name: apiVersion + type: + scalar: string + - name: description + type: + scalar: string + - name: globalDefault + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: value + type: + scalar: numeric +- name: io.k8s.api.scheduling.v1beta1.PriorityClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.scheduling.v1beta1.PriorityClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.settings.v1alpha1.PodPreset + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.settings.v1alpha1.PodPresetSpec +- name: io.k8s.api.settings.v1alpha1.PodPresetList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.settings.v1alpha1.PodPreset + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.settings.v1alpha1.PodPresetSpec + map: + fields: + - name: env + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvVar + elementRelationship: atomic + - name: envFrom + type: + list: + elementType: + namedType: io.k8s.api.core.v1.EnvFromSource + elementRelationship: atomic + - name: selector + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + - name: volumeMounts + type: + list: + elementType: + namedType: io.k8s.api.core.v1.VolumeMount + elementRelationship: atomic + - name: volumes + type: + list: + elementType: + namedType: io.k8s.api.core.v1.Volume + elementRelationship: atomic +- name: io.k8s.api.storage.v1.StorageClass + map: + fields: + - name: allowVolumeExpansion + type: + scalar: boolean + - name: allowedTopologies + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorTerm + elementRelationship: atomic + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: parameters + type: + map: + elementType: + scalar: string + - name: provisioner + type: + scalar: string + - name: reclaimPolicy + type: + scalar: string + - name: volumeBindingMode + type: + scalar: string +- name: io.k8s.api.storage.v1.StorageClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1.StorageClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.storage.v1alpha1.VolumeAttachment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec + - name: status + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentSource + map: + fields: + - name: persistentVolumeName + type: + scalar: string +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentSpec + map: + fields: + - name: attacher + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: source + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeAttachmentSource +- name: io.k8s.api.storage.v1alpha1.VolumeAttachmentStatus + map: + fields: + - name: attachError + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeError + - name: attached + type: + scalar: boolean + - name: attachmentMetadata + type: + map: + elementType: + scalar: string + - name: detachError + type: + namedType: io.k8s.api.storage.v1alpha1.VolumeError +- name: io.k8s.api.storage.v1alpha1.VolumeError + map: + fields: + - name: message + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.api.storage.v1beta1.StorageClass + map: + fields: + - name: allowVolumeExpansion + type: + scalar: boolean + - name: allowedTopologies + type: + list: + elementType: + namedType: io.k8s.api.core.v1.TopologySelectorTerm + elementRelationship: atomic + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: mountOptions + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: parameters + type: + map: + elementType: + scalar: string + - name: provisioner + type: + scalar: string + - name: reclaimPolicy + type: + scalar: string + - name: volumeBindingMode + type: + scalar: string +- name: io.k8s.api.storage.v1beta1.StorageClassList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1beta1.StorageClass + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.storage.v1beta1.VolumeAttachment + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentSpec + - name: status + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentStatus +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachment + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentSource + map: + fields: + - name: persistentVolumeName + type: + scalar: string +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentSpec + map: + fields: + - name: attacher + type: + scalar: string + - name: nodeName + type: + scalar: string + - name: source + type: + namedType: io.k8s.api.storage.v1beta1.VolumeAttachmentSource +- name: io.k8s.api.storage.v1beta1.VolumeAttachmentStatus + map: + fields: + - name: attachError + type: + namedType: io.k8s.api.storage.v1beta1.VolumeError + - name: attached + type: + scalar: boolean + - name: attachmentMetadata + type: + map: + elementType: + scalar: string + - name: detachError + type: + namedType: io.k8s.api.storage.v1beta1.VolumeError +- name: io.k8s.api.storage.v1beta1.VolumeError + map: + fields: + - name: message + type: + scalar: string + - name: time + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition + map: + fields: + - name: JSONPath + type: + scalar: string + - name: description + type: + scalar: string + - name: format + type: + scalar: string + - name: name + type: + scalar: string + - name: priority + type: + scalar: numeric + - name: type + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec + - name: status + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + map: + fields: + - name: categories + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: listKind + type: + scalar: string + - name: plural + type: + scalar: string + - name: shortNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: singular + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionSpec + map: + fields: + - name: additionalPrinterColumns + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition + elementRelationship: atomic + - name: group + type: + scalar: string + - name: names + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + - name: scope + type: + scalar: string + - name: subresources + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources + - name: validation + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation + elementRelationship: granular + - name: version + type: + scalar: string + - name: versions + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionStatus + map: + fields: + - name: acceptedNames + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames + - name: conditions + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition + elementRelationship: atomic + - name: storedVersions + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionVersion + map: + fields: + - name: name + type: + scalar: string + - name: served + type: + scalar: boolean + - name: storage + type: + scalar: boolean +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale + map: + fields: + - name: labelSelectorPath + type: + scalar: string + - name: specReplicasPath + type: + scalar: string + - name: statusReplicasPath + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresources + map: + fields: + - name: scale + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceScale + - name: status + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceSubresourceStatus +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceValidation + map: + fields: + - name: openAPIV3Schema + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation + map: + fields: + - name: description + type: + scalar: string + - name: url + type: + scalar: string +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + map: + fields: + - name: $ref + type: + scalar: string + - name: $schema + type: + scalar: string + - name: additionalItems + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + - name: additionalProperties + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + - name: allOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: anyOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: default + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + - name: definitions + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: dependencies + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray + - name: description + type: + scalar: string + - name: enum + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + elementRelationship: atomic + - name: example + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON + - name: exclusiveMaximum + type: + scalar: boolean + - name: exclusiveMinimum + type: + scalar: boolean + - name: externalDocs + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.ExternalDocumentation + - name: format + type: + scalar: string + - name: id + type: + scalar: string + - name: items + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray + - name: maxItems + type: + scalar: numeric + - name: maxLength + type: + scalar: numeric + - name: maxProperties + type: + scalar: numeric + - name: maximum + type: + scalar: numeric + - name: minItems + type: + scalar: numeric + - name: minLength + type: + scalar: numeric + - name: minProperties + type: + scalar: numeric + - name: minimum + type: + scalar: numeric + - name: multipleOf + type: + scalar: numeric + - name: not + type: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: oneOf + type: + list: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + elementRelationship: atomic + - name: pattern + type: + scalar: string + - name: patternProperties + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: properties + type: + map: + elementType: + namedType: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps + - name: required + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: title + type: + scalar: string + - name: type + type: + scalar: string + - name: uniqueItems + type: + scalar: boolean +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrArray + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.api.resource.Quantity + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: preferredVersion + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + - name: serverAddressByClientCIDRs + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + elementRelationship: atomic + - name: versions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroupList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: groups + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup + elementRelationship: atomic + - name: kind + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIResource + map: + fields: + - name: categories + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: group + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: namespaced + type: + scalar: boolean + - name: shortNames + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: singularName + type: + scalar: string + - name: verbs + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: version + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: groupVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: resources + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.APIResource + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.APIVersions + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: serverAddressByClientCIDRs + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + elementRelationship: atomic + - name: versions + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions + map: + fields: + - name: apiVersion + type: + scalar: string + - name: dryRun + type: + list: + elementType: + scalar: string + elementRelationship: atomic + - name: gracePeriodSeconds + type: + scalar: numeric + - name: kind + type: + scalar: string + - name: orphanDependents + type: + scalar: boolean + - name: preconditions + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions + - name: propagationPolicy + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery + map: + fields: + - name: groupVersion + type: + scalar: string + - name: version + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Initializer + map: + fields: + - name: name + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Initializers + map: + fields: + - name: pending + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Initializer + elementRelationship: associative + keys: + - name + - name: result + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Status +- name: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector + map: + fields: + - name: matchExpressions + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement + elementRelationship: atomic + - name: matchLabels + type: + map: + elementType: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement + map: + fields: + - name: key + type: + scalar: string + - name: operator + type: + scalar: string + - name: values + type: + list: + elementType: + scalar: string + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + map: + fields: + - name: continue + type: + scalar: string + - name: resourceVersion + type: + scalar: string + - name: selfLink + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.MicroTime + scalar: untyped +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + map: + fields: + - name: annotations + type: + map: + elementType: + scalar: string + - name: clusterName + type: + scalar: string + - name: creationTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: deletionGracePeriodSeconds + type: + scalar: numeric + - name: deletionTimestamp + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: finalizers + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: generateName + type: + scalar: string + - name: generation + type: + scalar: numeric + - name: initializers + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Initializers + - name: labels + type: + map: + elementType: + scalar: string + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string + - name: ownerReferences + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + elementRelationship: associative + keys: + - uid + - name: resourceVersion + type: + scalar: string + - name: selfLink + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference + map: + fields: + - name: apiVersion + type: + scalar: string + - name: blockOwnerDeletion + type: + scalar: boolean + - name: controller + type: + scalar: boolean + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Patch + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions + map: + fields: + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR + map: + fields: + - name: clientCIDR + type: + scalar: string + - name: serverAddress + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Status + map: + fields: + - name: apiVersion + type: + scalar: string + - name: code + type: + scalar: numeric + - name: details + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails + - name: kind + type: + scalar: string + - name: message + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + - name: reason + type: + scalar: string + - name: status + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause + map: + fields: + - name: field + type: + scalar: string + - name: message + type: + scalar: string + - name: reason + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails + map: + fields: + - name: causes + type: + list: + elementType: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause + elementRelationship: atomic + - name: group + type: + scalar: string + - name: kind + type: + scalar: string + - name: name + type: + scalar: string + - name: retryAfterSeconds + type: + scalar: numeric + - name: uid + type: + scalar: string +- name: io.k8s.apimachinery.pkg.apis.meta.v1.Time + scalar: untyped +- name: io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent + map: + fields: + - name: object + type: + namedType: __untyped_atomic_ + - name: type + type: + scalar: string +- name: io.k8s.apimachinery.pkg.runtime.RawExtension + map: + fields: + - name: Raw + type: + scalar: string +- name: io.k8s.apimachinery.pkg.util.intstr.IntOrString + scalar: untyped +- name: io.k8s.apimachinery.pkg.version.Info + map: + fields: + - name: buildDate + type: + scalar: string + - name: compiler + type: + scalar: string + - name: gitCommit + type: + scalar: string + - name: gitTreeState + type: + scalar: string + - name: gitVersion + type: + scalar: string + - name: goVersion + type: + scalar: string + - name: major + type: + scalar: string + - name: minor + type: + scalar: string + - name: platform + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + elementRelationship: granular + - name: spec + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec + - name: status + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIService + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta + elementRelationship: granular +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceSpec + map: + fields: + - name: caBundle + type: + scalar: string + - name: group + type: + scalar: string + - name: groupPriorityMinimum + type: + scalar: numeric + - name: insecureSkipTLSVerify + type: + scalar: boolean + - name: service + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference + - name: version + type: + scalar: string + - name: versionPriority + type: + scalar: numeric +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.APIServiceCondition + elementRelationship: associative + keys: + - type +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService + map: + fields: + - name: apiVersion + type: + scalar: string + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta + - name: spec + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec + elementRelationship: granular + - name: status + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition + map: + fields: + - name: lastTransitionTime + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time + - name: message + type: + scalar: string + - name: reason + type: + scalar: string + - name: status + type: + scalar: string + - name: type + type: + scalar: string +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceList + map: + fields: + - name: apiVersion + type: + scalar: string + - name: items + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIService + elementRelationship: atomic + - name: kind + type: + scalar: string + - name: metadata + type: + namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceSpec + map: + fields: + - name: caBundle + type: + scalar: string + - name: group + type: + scalar: string + - name: groupPriorityMinimum + type: + scalar: numeric + - name: insecureSkipTLSVerify + type: + scalar: boolean + - name: service + type: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference + - name: version + type: + scalar: string + - name: versionPriority + type: + scalar: numeric +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceStatus + map: + fields: + - name: conditions + type: + list: + elementType: + namedType: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.APIServiceCondition + elementRelationship: associative + keys: + - type +- name: io.k8s.kube-aggregator.pkg.apis.apiregistration.v1beta1.ServiceReference + map: + fields: + - name: name + type: + scalar: string + - name: namespace + type: + scalar: string +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic diff --git a/merge/real_test.go b/merge/real_test.go index 81098588..2620c43d 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -37,53 +37,75 @@ func read(file string) []byte { return s } -var k8s = func() Parser { - s := read(testdata("k8s-schema.yaml")) +func loadParser(name string) Parser { + s := read(testdata(name)) parser, err := typed.NewParser(typed.YAMLObject(s)) if err != nil { panic(err) } return parser -}() +} -var apiresourceimport = func() Parser { - s := read(testdata("apiresourceimport.yaml")) - parser, err := typed.NewParser(typed.YAMLObject(s)) - if err != nil { - panic(err) - } - return parser -}() +var k8s = loadParser("k8s-schema.yaml") +var apiresourceimport = loadParser("apiresourceimport.yaml") +var k8s100pctOverrides = loadParser("k8s-schema-100pct-fieldoverride.yaml") +var k8s10pctOverrides = loadParser("k8s-schema-10pct-fieldoverride.yaml") func BenchmarkOperations(b *testing.B) { benches := []struct { + name string parseType typed.ParseableType filename string }{ { + name: "Pod", parseType: k8s.Type("io.k8s.api.core.v1.Pod"), filename: "pod.yaml", }, { + name: "Node", parseType: k8s.Type("io.k8s.api.core.v1.Node"), filename: "node.yaml", }, { + name: "Endpoints", parseType: k8s.Type("io.k8s.api.core.v1.Endpoints"), filename: "endpoints.yaml", }, { + name: "Node100%override", + parseType: k8s100pctOverrides.Type("io.k8s.api.core.v1.Node"), + filename: "node.yaml", + }, + { + name: "Node10%override", + parseType: k8s10pctOverrides.Type("io.k8s.api.core.v1.Node"), + filename: "node.yaml", + }, + { + name: "Endpoints100%override", + parseType: k8s100pctOverrides.Type("io.k8s.api.core.v1.Endpoints"), + filename: "endpoints.yaml", + }, + { + name: "Endpoints10%override", + parseType: k8s10pctOverrides.Type("io.k8s.api.core.v1.Endpoints"), + filename: "endpoints.yaml", + }, + { + name: "PrometheusCRD", parseType: k8s.Type("io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition"), filename: "prometheus-crd.yaml", }, { + name: "apiresourceimport", parseType: apiresourceimport.Type("apiresourceimport"), filename: "apiresourceimport-cr.yaml", }, } for _, bench := range benches { - b.Run(bench.filename, func(b *testing.B) { + b.Run(bench.name, func(b *testing.B) { obj := typed.YAMLObject(read(testdata(bench.filename))) tests := []struct { name string From 50567d18d2744676efa6fdc38a42b2c1da6e8753 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Thu, 28 Jul 2022 16:21:01 -0700 Subject: [PATCH 024/105] update gofmt --- schema/elements.go | 6 +++--- typed/reconcile_schema.go | 2 +- typed/typed.go | 13 +++++++------ 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/schema/elements.go b/schema/elements.go index 5d8e72af..34b504df 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -101,11 +101,11 @@ const ( // Map is a key-value pair. Its default semantics are the same as an // associative list, but: -// * It is serialized differently: +// - It is serialized differently: // map: {"k": {"value": "v"}} // list: [{"key": "k", "value": "v"}] -// * Keys must be string typed. -// * Keys can't have multiple components. +// - Keys must be string typed. +// - Keys can't have multiple components. // // Optionally, maps may be atomic (for example, imagine representing an RGB // color value--it doesn't make sense to have different actors own the R and G diff --git a/typed/reconcile_schema.go b/typed/reconcile_schema.go index 2b98b729..85325527 100644 --- a/typed/reconcile_schema.go +++ b/typed/reconcile_schema.go @@ -110,7 +110,7 @@ func (v *reconcileWithSchemaWalker) finishDescent(v2 *reconcileWithSchemaWalker) } // ReconcileFieldSetWithSchema reconciles the a field set with any changes to the -//// object's schema since the field set was written. Returns the reconciled field set, or nil of +// // object's schema since the field set was written. Returns the reconciled field set, or nil of // no changes were made to the field set. // // Supports: diff --git a/typed/typed.go b/typed/typed.go index e9e6be8b..6b32adab 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -99,12 +99,13 @@ func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) { // Merge returns the result of merging tv and pso ("partially specified // object") together. Of note: -// * No fields can be removed by this operation. -// * If both tv and pso specify a given leaf field, the result will keep pso's -// value. -// * Container typed elements will have their items ordered: -// * like tv, if pso doesn't change anything in the container -// * like pso, if pso does change something in the container. +// - No fields can be removed by this operation. +// - If both tv and pso specify a given leaf field, the result will keep pso's +// value. +// - Container typed elements will have their items ordered: +// - like tv, if pso doesn't change anything in the container +// - like pso, if pso does change something in the container. +// // tv and pso must both be of the same type (their Schema and TypeRef must // match), or an error will be returned. Validation errors will be returned if // the objects don't conform to the schema. From c779eba8f9cb9ef997ef9601279ec0d91d9d0eb1 Mon Sep 17 00:00:00 2001 From: Alexander Zielenski <351783+alexzielenski@users.noreply.github.com> Date: Thu, 28 Jul 2022 16:48:42 -0700 Subject: [PATCH 025/105] fix gofmt unfortunately cant nest indentation with an unordered list --- typed/reconcile_schema.go | 2 +- typed/typed.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/typed/reconcile_schema.go b/typed/reconcile_schema.go index 85325527..6a7697e3 100644 --- a/typed/reconcile_schema.go +++ b/typed/reconcile_schema.go @@ -110,7 +110,7 @@ func (v *reconcileWithSchemaWalker) finishDescent(v2 *reconcileWithSchemaWalker) } // ReconcileFieldSetWithSchema reconciles the a field set with any changes to the -// // object's schema since the field set was written. Returns the reconciled field set, or nil of +// object's schema since the field set was written. Returns the reconciled field set, or nil of // no changes were made to the field set. // // Supports: diff --git a/typed/typed.go b/typed/typed.go index 6b32adab..d63a97fe 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -103,8 +103,8 @@ func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) { // - If both tv and pso specify a given leaf field, the result will keep pso's // value. // - Container typed elements will have their items ordered: -// - like tv, if pso doesn't change anything in the container -// - like pso, if pso does change something in the container. +// 1. like tv, if pso doesn't change anything in the container +// 2. like pso, if pso does change something in the container. // // tv and pso must both be of the same type (their Schema and TypeRef must // match), or an error will be returned. Validation errors will be returned if From 1754a8d8ea7a93c60dba978c107b05c4a58fd641 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 1 Aug 2022 09:43:59 -0700 Subject: [PATCH 026/105] Create package v5 for backward incompatible change --- fieldpath/element.go | 2 +- fieldpath/element_test.go | 2 +- fieldpath/fromvalue.go | 2 +- fieldpath/fromvalue_test.go | 2 +- fieldpath/managers_test.go | 2 +- fieldpath/path.go | 2 +- fieldpath/path_test.go | 2 +- fieldpath/pathelementmap.go | 2 +- fieldpath/pathelementmap_test.go | 2 +- fieldpath/serialize-pe.go | 2 +- fieldpath/set.go | 2 +- fieldpath/set_test.go | 2 +- go.mod | 2 +- internal/cli/operation.go | 4 ++-- internal/cli/options.go | 2 +- internal/fixture/state.go | 8 ++++---- internal/fixture/state_test.go | 2 +- merge/conflict.go | 2 +- merge/conflict_test.go | 6 +++--- merge/deduced_test.go | 6 +++--- merge/default_keys_test.go | 8 ++++---- merge/extract_apply_test.go | 6 +++--- merge/field_level_overrides_test.go | 8 ++++---- merge/ignore_test.go | 4 ++-- merge/key_test.go | 6 +++--- merge/leaf_test.go | 8 ++++---- merge/multiple_appliers_test.go | 10 +++++----- merge/nested_test.go | 6 +++--- merge/obsolete_versions_test.go | 8 ++++---- merge/preserve_unknown_test.go | 6 +++--- merge/real_test.go | 4 ++-- merge/schema_change_test.go | 8 ++++---- merge/set_test.go | 6 +++--- merge/union_test.go | 8 ++++---- merge/update.go | 4 ++-- smd/main.go | 2 +- typed/comparison_test.go | 4 ++-- typed/deduced_test.go | 4 ++-- typed/helpers.go | 6 +++--- typed/helpers_test.go | 4 ++-- typed/merge.go | 6 +++--- typed/merge_test.go | 4 ++-- typed/parser.go | 4 ++-- typed/parser_test.go | 2 +- typed/reconcile_schema.go | 4 ++-- typed/reconcile_schema_test.go | 4 ++-- typed/remove.go | 6 +++--- typed/remove_test.go | 6 +++--- typed/symdiff_test.go | 4 ++-- typed/tofieldset.go | 6 +++--- typed/toset_test.go | 6 +++--- typed/typed.go | 6 +++--- typed/union.go | 4 ++-- typed/union_test.go | 2 +- typed/validate.go | 6 +++--- typed/validate_test.go | 4 ++-- 56 files changed, 125 insertions(+), 125 deletions(-) diff --git a/fieldpath/element.go b/fieldpath/element.go index 1578f64c..280d0ea6 100644 --- a/fieldpath/element.go +++ b/fieldpath/element.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // PathElement describes how to select a child field given a containing object. diff --git a/fieldpath/element_test.go b/fieldpath/element_test.go index 05019a98..794bfdee 100644 --- a/fieldpath/element_test.go +++ b/fieldpath/element_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func TestPathElementSet(t *testing.T) { diff --git a/fieldpath/fromvalue.go b/fieldpath/fromvalue.go index 20775ee0..295d2685 100644 --- a/fieldpath/fromvalue.go +++ b/fieldpath/fromvalue.go @@ -17,7 +17,7 @@ limitations under the License. package fieldpath import ( - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // SetFromValue creates a set containing every leaf field mentioned in v. diff --git a/fieldpath/fromvalue_test.go b/fieldpath/fromvalue_test.go index 1cbf0b1b..92de84e3 100644 --- a/fieldpath/fromvalue_test.go +++ b/fieldpath/fromvalue_test.go @@ -20,7 +20,7 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func TestFromValue(t *testing.T) { diff --git a/fieldpath/managers_test.go b/fieldpath/managers_test.go index c3a9b2cf..432cd590 100644 --- a/fieldpath/managers_test.go +++ b/fieldpath/managers_test.go @@ -21,7 +21,7 @@ import ( "reflect" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" ) var ( diff --git a/fieldpath/path.go b/fieldpath/path.go index 0413130b..0a457fd9 100644 --- a/fieldpath/path.go +++ b/fieldpath/path.go @@ -20,7 +20,7 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // Path describes how to select a potentially deeply-nested child field given a diff --git a/fieldpath/path_test.go b/fieldpath/path_test.go index d3086732..2f50173a 100644 --- a/fieldpath/path_test.go +++ b/fieldpath/path_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) var ( diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index 9b14ca58..ba4a0399 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -19,7 +19,7 @@ package fieldpath import ( "sort" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // PathElementValueMap is a map from PathElement to value.Value. diff --git a/fieldpath/pathelementmap_test.go b/fieldpath/pathelementmap_test.go index cbabf50f..b635771a 100644 --- a/fieldpath/pathelementmap_test.go +++ b/fieldpath/pathelementmap_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func TestPathElementValueMap(t *testing.T) { diff --git a/fieldpath/serialize-pe.go b/fieldpath/serialize-pe.go index cb18e7b1..2904051a 100644 --- a/fieldpath/serialize-pe.go +++ b/fieldpath/serialize-pe.go @@ -24,7 +24,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/value" ) var ErrUnknownPathElementType = errors.New("unknown path element type") diff --git a/fieldpath/set.go b/fieldpath/set.go index 6d182768..ac7de802 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -20,7 +20,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v5/schema" ) // Set identifies a set of fields. diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index 60f97bd7..364c4e78 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -23,7 +23,7 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v5/schema" ) type randomPathAlphabet []PathElement diff --git a/go.mod b/go.mod index 0f5fe4b5..5d0ac105 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module sigs.k8s.io/structured-merge-diff/v4 +module sigs.k8s.io/structured-merge-diff/v5 require gopkg.in/yaml.v2 v2.2.8 diff --git a/internal/cli/operation.go b/internal/cli/operation.go index 53789731..84185bbd 100644 --- a/internal/cli/operation.go +++ b/internal/cli/operation.go @@ -21,8 +21,8 @@ import ( "io" "io/ioutil" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type Operation interface { diff --git a/internal/cli/options.go b/internal/cli/options.go index 9a4cbeb8..ce46053d 100644 --- a/internal/cli/options.go +++ b/internal/cli/options.go @@ -24,7 +24,7 @@ import ( "io/ioutil" "os" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var ( diff --git a/internal/fixture/state.go b/internal/fixture/state.go index b37a5e3b..619951b3 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -20,10 +20,10 @@ import ( "bytes" "fmt" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // For the sake of tests, a parser is something that can retrieve a diff --git a/internal/fixture/state_test.go b/internal/fixture/state_test.go index 0b88f755..745681d2 100644 --- a/internal/fixture/state_test.go +++ b/internal/fixture/state_test.go @@ -20,7 +20,7 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func TestFixTabs(t *testing.T) { diff --git a/merge/conflict.go b/merge/conflict.go index 75a492d8..6df10f33 100644 --- a/merge/conflict.go +++ b/merge/conflict.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" ) // Conflict is a conflict on a specific field with the current manager of diff --git a/merge/conflict_test.go b/merge/conflict_test.go index f674f87c..90c0f916 100644 --- a/merge/conflict_test.go +++ b/merge/conflict_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/value" ) var ( diff --git a/merge/deduced_test.go b/merge/deduced_test.go index 3bde04b9..6b0b1525 100644 --- a/merge/deduced_test.go +++ b/merge/deduced_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" ) func TestDeduced(t *testing.T) { diff --git a/merge/default_keys_test.go b/merge/default_keys_test.go index 33a62b83..3ea881aa 100644 --- a/merge/default_keys_test.go +++ b/merge/default_keys_test.go @@ -16,10 +16,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) // portListParser sets the default value of key "protocol" to "TCP" diff --git a/merge/extract_apply_test.go b/merge/extract_apply_test.go index 50e19e13..caef7564 100644 --- a/merge/extract_apply_test.go +++ b/merge/extract_apply_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var extractParser = func() Parser { diff --git a/merge/field_level_overrides_test.go b/merge/field_level_overrides_test.go index 75ac3256..2462eba2 100644 --- a/merge/field_level_overrides_test.go +++ b/merge/field_level_overrides_test.go @@ -3,10 +3,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func TestFieldLevelOverrides(t *testing.T) { diff --git a/merge/ignore_test.go b/merge/ignore_test.go index 4cf7a918..4fe75c19 100644 --- a/merge/ignore_test.go +++ b/merge/ignore_test.go @@ -19,8 +19,8 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" ) func TestIgnoredFields(t *testing.T) { diff --git a/merge/key_test.go b/merge/key_test.go index e76e67cb..71387dcd 100644 --- a/merge/key_test.go +++ b/merge/key_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var associativeListParser = func() Parser { diff --git a/merge/leaf_test.go b/merge/leaf_test.go index ab6d59ae..da8299f6 100644 --- a/merge/leaf_test.go +++ b/merge/leaf_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var leafFieldsParser = func() Parser { diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index 9ebcc662..ffed1d03 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -23,11 +23,11 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func TestMultipleAppliersSet(t *testing.T) { diff --git a/merge/nested_test.go b/merge/nested_test.go index c87c46bb..e9b18312 100644 --- a/merge/nested_test.go +++ b/merge/nested_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var nestedTypeParser = func() Parser { diff --git a/merge/obsolete_versions_test.go b/merge/obsolete_versions_test.go index fdb119eb..b7936325 100644 --- a/merge/obsolete_versions_test.go +++ b/merge/obsolete_versions_test.go @@ -20,10 +20,10 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) // specificVersionConverter doesn't convert and return the exact same diff --git a/merge/preserve_unknown_test.go b/merge/preserve_unknown_test.go index 2d77384f..121f2a77 100644 --- a/merge/preserve_unknown_test.go +++ b/merge/preserve_unknown_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var preserveUnknownParser = func() Parser { diff --git a/merge/real_test.go b/merge/real_test.go index 2620c43d..2a5a9f01 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -21,8 +21,8 @@ import ( "path/filepath" "testing" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func testdata(file string) string { diff --git a/merge/schema_change_test.go b/merge/schema_change_test.go index f98fdf73..be24bb2d 100644 --- a/merge/schema_change_test.go +++ b/merge/schema_change_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var structParser = func() *typed.Parser { diff --git a/merge/set_test.go b/merge/set_test.go index 5882c6ef..b1bfac29 100644 --- a/merge/set_test.go +++ b/merge/set_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var setFieldsParser = func() Parser { diff --git a/merge/union_test.go b/merge/union_test.go index 9a8ba56b..d6989568 100644 --- a/merge/union_test.go +++ b/merge/union_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var unionFieldsParser = func() Parser { diff --git a/merge/update.go b/merge/update.go index 1b23dcbd..190d6997 100644 --- a/merge/update.go +++ b/merge/update.go @@ -16,8 +16,8 @@ package merge import ( "fmt" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) // Converter is an interface to the conversion logic. The converter diff --git a/smd/main.go b/smd/main.go index 34a904e9..17693f8b 100644 --- a/smd/main.go +++ b/smd/main.go @@ -22,7 +22,7 @@ import ( "flag" "log" - "sigs.k8s.io/structured-merge-diff/v4/internal/cli" + "sigs.k8s.io/structured-merge-diff/v5/internal/cli" ) func main() { diff --git a/typed/comparison_test.go b/typed/comparison_test.go index 321f90cc..1334a5ca 100644 --- a/typed/comparison_test.go +++ b/typed/comparison_test.go @@ -3,8 +3,8 @@ package typed_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func TestComparisonExcludeFields(t *testing.T) { diff --git a/typed/deduced_test.go b/typed/deduced_test.go index 0719eae1..670b70c5 100644 --- a/typed/deduced_test.go +++ b/typed/deduced_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func TestValidateDeducedType(t *testing.T) { diff --git a/typed/helpers.go b/typed/helpers.go index 19c77334..f58cd67a 100644 --- a/typed/helpers.go +++ b/typed/helpers.go @@ -21,9 +21,9 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // ValidationError reports an error about a particular field diff --git a/typed/helpers_test.go b/typed/helpers_test.go index d04d4339..dfc431d5 100644 --- a/typed/helpers_test.go +++ b/typed/helpers_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func TestInvalidOverride(t *testing.T) { diff --git a/typed/merge.go b/typed/merge.go index 91364408..bcfa2472 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -17,9 +17,9 @@ limitations under the License. package typed import ( - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type mergingWalker struct { diff --git a/typed/merge_test.go b/typed/merge_test.go index 63745cdb..5e62cd4d 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type mergeTestCase struct { diff --git a/typed/parser.go b/typed/parser.go index 1e657ef7..83af2d10 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -20,8 +20,8 @@ import ( "fmt" yaml "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // YAMLObject is an object encoded in YAML. diff --git a/typed/parser_test.go b/typed/parser_test.go index fdb7d9fc..d874fc08 100644 --- a/typed/parser_test.go +++ b/typed/parser_test.go @@ -23,7 +23,7 @@ import ( "testing" yaml "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) func testdata(file string) string { diff --git a/typed/reconcile_schema.go b/typed/reconcile_schema.go index 6a7697e3..4f9d2bca 100644 --- a/typed/reconcile_schema.go +++ b/typed/reconcile_schema.go @@ -20,8 +20,8 @@ import ( "fmt" "sync" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" ) var fmPool = sync.Pool{ diff --git a/typed/reconcile_schema_test.go b/typed/reconcile_schema_test.go index 72053af2..c244b8b1 100644 --- a/typed/reconcile_schema_test.go +++ b/typed/reconcile_schema_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) type reconcileTestCase struct { diff --git a/typed/remove.go b/typed/remove.go index a338d761..020b0fbc 100644 --- a/typed/remove.go +++ b/typed/remove.go @@ -14,9 +14,9 @@ limitations under the License. package typed import ( - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type removingWalker struct { diff --git a/typed/remove_test.go b/typed/remove_test.go index e31ee06d..ae71b664 100644 --- a/typed/remove_test.go +++ b/typed/remove_test.go @@ -20,9 +20,9 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type removeTestCase struct { diff --git a/typed/symdiff_test.go b/typed/symdiff_test.go index 57751a73..b5e69a4d 100644 --- a/typed/symdiff_test.go +++ b/typed/symdiff_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) type symdiffTestCase struct { diff --git a/typed/tofieldset.go b/typed/tofieldset.go index 047efff0..015f1110 100644 --- a/typed/tofieldset.go +++ b/typed/tofieldset.go @@ -19,9 +19,9 @@ package typed import ( "sync" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) var tPool = sync.Pool{ diff --git a/typed/toset_test.go b/typed/toset_test.go index 2cc6810c..554ba901 100644 --- a/typed/toset_test.go +++ b/typed/toset_test.go @@ -20,9 +20,9 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v5/value" ) type objSetPair struct { diff --git a/typed/typed.go b/typed/typed.go index d63a97fe..b66fccb2 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -21,9 +21,9 @@ import ( "strings" "sync" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) // AsTyped accepts a value and a type and returns a TypedValue. 'v' must have diff --git a/typed/union.go b/typed/union.go index 1fa5d88a..ee04bf22 100644 --- a/typed/union.go +++ b/typed/union.go @@ -20,8 +20,8 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) func normalizeUnions(w *mergingWalker) error { diff --git a/typed/union_test.go b/typed/union_test.go index a3ed129a..09f7335d 100644 --- a/typed/union_test.go +++ b/typed/union_test.go @@ -19,7 +19,7 @@ package typed_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) var unionParser = func() typed.ParseableType { diff --git a/typed/validate.go b/typed/validate.go index 378d3021..b7332fc7 100644 --- a/typed/validate.go +++ b/typed/validate.go @@ -19,9 +19,9 @@ package typed import ( "sync" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" + "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/value" ) var vPool = sync.Pool{ diff --git a/typed/validate_test.go b/typed/validate_test.go index 43357c5c..ac54e4c8 100644 --- a/typed/validate_test.go +++ b/typed/validate_test.go @@ -21,8 +21,8 @@ import ( "strings" "testing" - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v5/typed" ) type validationTestCase struct { From a70144bcc14c52b7e554c2c5ad57aafd42ef5414 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 1 Aug 2022 16:45:28 -0700 Subject: [PATCH 027/105] Revert "Create package v5 for backward incompatible change" This reverts commit 1754a8d8ea7a93c60dba978c107b05c4a58fd641. --- fieldpath/element.go | 2 +- fieldpath/element_test.go | 2 +- fieldpath/fromvalue.go | 2 +- fieldpath/fromvalue_test.go | 2 +- fieldpath/managers_test.go | 2 +- fieldpath/path.go | 2 +- fieldpath/path_test.go | 2 +- fieldpath/pathelementmap.go | 2 +- fieldpath/pathelementmap_test.go | 2 +- fieldpath/serialize-pe.go | 2 +- fieldpath/set.go | 2 +- fieldpath/set_test.go | 2 +- go.mod | 2 +- internal/cli/operation.go | 4 ++-- internal/cli/options.go | 2 +- internal/fixture/state.go | 8 ++++---- internal/fixture/state_test.go | 2 +- merge/conflict.go | 2 +- merge/conflict_test.go | 6 +++--- merge/deduced_test.go | 6 +++--- merge/default_keys_test.go | 8 ++++---- merge/extract_apply_test.go | 6 +++--- merge/field_level_overrides_test.go | 8 ++++---- merge/ignore_test.go | 4 ++-- merge/key_test.go | 6 +++--- merge/leaf_test.go | 8 ++++---- merge/multiple_appliers_test.go | 10 +++++----- merge/nested_test.go | 6 +++--- merge/obsolete_versions_test.go | 8 ++++---- merge/preserve_unknown_test.go | 6 +++--- merge/real_test.go | 4 ++-- merge/schema_change_test.go | 8 ++++---- merge/set_test.go | 6 +++--- merge/union_test.go | 8 ++++---- merge/update.go | 4 ++-- smd/main.go | 2 +- typed/comparison_test.go | 4 ++-- typed/deduced_test.go | 4 ++-- typed/helpers.go | 6 +++--- typed/helpers_test.go | 4 ++-- typed/merge.go | 6 +++--- typed/merge_test.go | 4 ++-- typed/parser.go | 4 ++-- typed/parser_test.go | 2 +- typed/reconcile_schema.go | 4 ++-- typed/reconcile_schema_test.go | 4 ++-- typed/remove.go | 6 +++--- typed/remove_test.go | 6 +++--- typed/symdiff_test.go | 4 ++-- typed/tofieldset.go | 6 +++--- typed/toset_test.go | 6 +++--- typed/typed.go | 6 +++--- typed/union.go | 4 ++-- typed/union_test.go | 2 +- typed/validate.go | 6 +++--- typed/validate_test.go | 4 ++-- 56 files changed, 125 insertions(+), 125 deletions(-) diff --git a/fieldpath/element.go b/fieldpath/element.go index 280d0ea6..1578f64c 100644 --- a/fieldpath/element.go +++ b/fieldpath/element.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // PathElement describes how to select a child field given a containing object. diff --git a/fieldpath/element_test.go b/fieldpath/element_test.go index 794bfdee..05019a98 100644 --- a/fieldpath/element_test.go +++ b/fieldpath/element_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func TestPathElementSet(t *testing.T) { diff --git a/fieldpath/fromvalue.go b/fieldpath/fromvalue.go index 295d2685..20775ee0 100644 --- a/fieldpath/fromvalue.go +++ b/fieldpath/fromvalue.go @@ -17,7 +17,7 @@ limitations under the License. package fieldpath import ( - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // SetFromValue creates a set containing every leaf field mentioned in v. diff --git a/fieldpath/fromvalue_test.go b/fieldpath/fromvalue_test.go index 92de84e3..1cbf0b1b 100644 --- a/fieldpath/fromvalue_test.go +++ b/fieldpath/fromvalue_test.go @@ -20,7 +20,7 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func TestFromValue(t *testing.T) { diff --git a/fieldpath/managers_test.go b/fieldpath/managers_test.go index 432cd590..c3a9b2cf 100644 --- a/fieldpath/managers_test.go +++ b/fieldpath/managers_test.go @@ -21,7 +21,7 @@ import ( "reflect" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) var ( diff --git a/fieldpath/path.go b/fieldpath/path.go index 0a457fd9..0413130b 100644 --- a/fieldpath/path.go +++ b/fieldpath/path.go @@ -20,7 +20,7 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // Path describes how to select a potentially deeply-nested child field given a diff --git a/fieldpath/path_test.go b/fieldpath/path_test.go index 2f50173a..d3086732 100644 --- a/fieldpath/path_test.go +++ b/fieldpath/path_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) var ( diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index ba4a0399..9b14ca58 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -19,7 +19,7 @@ package fieldpath import ( "sort" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // PathElementValueMap is a map from PathElement to value.Value. diff --git a/fieldpath/pathelementmap_test.go b/fieldpath/pathelementmap_test.go index b635771a..cbabf50f 100644 --- a/fieldpath/pathelementmap_test.go +++ b/fieldpath/pathelementmap_test.go @@ -19,7 +19,7 @@ package fieldpath import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func TestPathElementValueMap(t *testing.T) { diff --git a/fieldpath/serialize-pe.go b/fieldpath/serialize-pe.go index 2904051a..cb18e7b1 100644 --- a/fieldpath/serialize-pe.go +++ b/fieldpath/serialize-pe.go @@ -24,7 +24,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/value" ) var ErrUnknownPathElementType = errors.New("unknown path element type") diff --git a/fieldpath/set.go b/fieldpath/set.go index ac7de802..6d182768 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -20,7 +20,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v4/schema" ) // Set identifies a set of fields. diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index 364c4e78..60f97bd7 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -23,7 +23,7 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v4/schema" ) type randomPathAlphabet []PathElement diff --git a/go.mod b/go.mod index 5d0ac105..0f5fe4b5 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module sigs.k8s.io/structured-merge-diff/v5 +module sigs.k8s.io/structured-merge-diff/v4 require gopkg.in/yaml.v2 v2.2.8 diff --git a/internal/cli/operation.go b/internal/cli/operation.go index 84185bbd..53789731 100644 --- a/internal/cli/operation.go +++ b/internal/cli/operation.go @@ -21,8 +21,8 @@ import ( "io" "io/ioutil" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type Operation interface { diff --git a/internal/cli/options.go b/internal/cli/options.go index ce46053d..9a4cbeb8 100644 --- a/internal/cli/options.go +++ b/internal/cli/options.go @@ -24,7 +24,7 @@ import ( "io/ioutil" "os" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var ( diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 619951b3..b37a5e3b 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -20,10 +20,10 @@ import ( "bytes" "fmt" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // For the sake of tests, a parser is something that can retrieve a diff --git a/internal/fixture/state_test.go b/internal/fixture/state_test.go index 745681d2..0b88f755 100644 --- a/internal/fixture/state_test.go +++ b/internal/fixture/state_test.go @@ -20,7 +20,7 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func TestFixTabs(t *testing.T) { diff --git a/merge/conflict.go b/merge/conflict.go index 6df10f33..75a492d8 100644 --- a/merge/conflict.go +++ b/merge/conflict.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) // Conflict is a conflict on a specific field with the current manager of diff --git a/merge/conflict_test.go b/merge/conflict_test.go index 90c0f916..f674f87c 100644 --- a/merge/conflict_test.go +++ b/merge/conflict_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/value" ) var ( diff --git a/merge/deduced_test.go b/merge/deduced_test.go index 6b0b1525..3bde04b9 100644 --- a/merge/deduced_test.go +++ b/merge/deduced_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" ) func TestDeduced(t *testing.T) { diff --git a/merge/default_keys_test.go b/merge/default_keys_test.go index 3ea881aa..33a62b83 100644 --- a/merge/default_keys_test.go +++ b/merge/default_keys_test.go @@ -16,10 +16,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) // portListParser sets the default value of key "protocol" to "TCP" diff --git a/merge/extract_apply_test.go b/merge/extract_apply_test.go index caef7564..50e19e13 100644 --- a/merge/extract_apply_test.go +++ b/merge/extract_apply_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var extractParser = func() Parser { diff --git a/merge/field_level_overrides_test.go b/merge/field_level_overrides_test.go index 2462eba2..75ac3256 100644 --- a/merge/field_level_overrides_test.go +++ b/merge/field_level_overrides_test.go @@ -3,10 +3,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func TestFieldLevelOverrides(t *testing.T) { diff --git a/merge/ignore_test.go b/merge/ignore_test.go index 4fe75c19..4cf7a918 100644 --- a/merge/ignore_test.go +++ b/merge/ignore_test.go @@ -19,8 +19,8 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" ) func TestIgnoredFields(t *testing.T) { diff --git a/merge/key_test.go b/merge/key_test.go index 71387dcd..e76e67cb 100644 --- a/merge/key_test.go +++ b/merge/key_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var associativeListParser = func() Parser { diff --git a/merge/leaf_test.go b/merge/leaf_test.go index da8299f6..ab6d59ae 100644 --- a/merge/leaf_test.go +++ b/merge/leaf_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var leafFieldsParser = func() Parser { diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index ffed1d03..9ebcc662 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -23,11 +23,11 @@ import ( "testing" "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func TestMultipleAppliersSet(t *testing.T) { diff --git a/merge/nested_test.go b/merge/nested_test.go index e9b18312..c87c46bb 100644 --- a/merge/nested_test.go +++ b/merge/nested_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var nestedTypeParser = func() Parser { diff --git a/merge/obsolete_versions_test.go b/merge/obsolete_versions_test.go index b7936325..fdb119eb 100644 --- a/merge/obsolete_versions_test.go +++ b/merge/obsolete_versions_test.go @@ -20,10 +20,10 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) // specificVersionConverter doesn't convert and return the exact same diff --git a/merge/preserve_unknown_test.go b/merge/preserve_unknown_test.go index 121f2a77..2d77384f 100644 --- a/merge/preserve_unknown_test.go +++ b/merge/preserve_unknown_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var preserveUnknownParser = func() Parser { diff --git a/merge/real_test.go b/merge/real_test.go index 2a5a9f01..2620c43d 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -21,8 +21,8 @@ import ( "path/filepath" "testing" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func testdata(file string) string { diff --git a/merge/schema_change_test.go b/merge/schema_change_test.go index be24bb2d..f98fdf73 100644 --- a/merge/schema_change_test.go +++ b/merge/schema_change_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var structParser = func() *typed.Parser { diff --git a/merge/set_test.go b/merge/set_test.go index b1bfac29..5882c6ef 100644 --- a/merge/set_test.go +++ b/merge/set_test.go @@ -19,9 +19,9 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var setFieldsParser = func() Parser { diff --git a/merge/union_test.go b/merge/union_test.go index d6989568..9a8ba56b 100644 --- a/merge/union_test.go +++ b/merge/union_test.go @@ -19,10 +19,10 @@ package merge_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/merge" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var unionFieldsParser = func() Parser { diff --git a/merge/update.go b/merge/update.go index 190d6997..1b23dcbd 100644 --- a/merge/update.go +++ b/merge/update.go @@ -16,8 +16,8 @@ package merge import ( "fmt" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) // Converter is an interface to the conversion logic. The converter diff --git a/smd/main.go b/smd/main.go index 17693f8b..34a904e9 100644 --- a/smd/main.go +++ b/smd/main.go @@ -22,7 +22,7 @@ import ( "flag" "log" - "sigs.k8s.io/structured-merge-diff/v5/internal/cli" + "sigs.k8s.io/structured-merge-diff/v4/internal/cli" ) func main() { diff --git a/typed/comparison_test.go b/typed/comparison_test.go index 1334a5ca..321f90cc 100644 --- a/typed/comparison_test.go +++ b/typed/comparison_test.go @@ -3,8 +3,8 @@ package typed_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func TestComparisonExcludeFields(t *testing.T) { diff --git a/typed/deduced_test.go b/typed/deduced_test.go index 670b70c5..0719eae1 100644 --- a/typed/deduced_test.go +++ b/typed/deduced_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func TestValidateDeducedType(t *testing.T) { diff --git a/typed/helpers.go b/typed/helpers.go index f58cd67a..19c77334 100644 --- a/typed/helpers.go +++ b/typed/helpers.go @@ -21,9 +21,9 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // ValidationError reports an error about a particular field diff --git a/typed/helpers_test.go b/typed/helpers_test.go index dfc431d5..d04d4339 100644 --- a/typed/helpers_test.go +++ b/typed/helpers_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - "sigs.k8s.io/structured-merge-diff/v5/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func TestInvalidOverride(t *testing.T) { diff --git a/typed/merge.go b/typed/merge.go index bcfa2472..91364408 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -17,9 +17,9 @@ limitations under the License. package typed import ( - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type mergingWalker struct { diff --git a/typed/merge_test.go b/typed/merge_test.go index 5e62cd4d..63745cdb 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type mergeTestCase struct { diff --git a/typed/parser.go b/typed/parser.go index 83af2d10..1e657ef7 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -20,8 +20,8 @@ import ( "fmt" yaml "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // YAMLObject is an object encoded in YAML. diff --git a/typed/parser_test.go b/typed/parser_test.go index d874fc08..fdb7d9fc 100644 --- a/typed/parser_test.go +++ b/typed/parser_test.go @@ -23,7 +23,7 @@ import ( "testing" yaml "gopkg.in/yaml.v2" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) func testdata(file string) string { diff --git a/typed/reconcile_schema.go b/typed/reconcile_schema.go index 4f9d2bca..6a7697e3 100644 --- a/typed/reconcile_schema.go +++ b/typed/reconcile_schema.go @@ -20,8 +20,8 @@ import ( "fmt" "sync" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" ) var fmPool = sync.Pool{ diff --git a/typed/reconcile_schema_test.go b/typed/reconcile_schema_test.go index c244b8b1..72053af2 100644 --- a/typed/reconcile_schema_test.go +++ b/typed/reconcile_schema_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) type reconcileTestCase struct { diff --git a/typed/remove.go b/typed/remove.go index 020b0fbc..a338d761 100644 --- a/typed/remove.go +++ b/typed/remove.go @@ -14,9 +14,9 @@ limitations under the License. package typed import ( - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type removingWalker struct { diff --git a/typed/remove_test.go b/typed/remove_test.go index ae71b664..e31ee06d 100644 --- a/typed/remove_test.go +++ b/typed/remove_test.go @@ -20,9 +20,9 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type removeTestCase struct { diff --git a/typed/symdiff_test.go b/typed/symdiff_test.go index b5e69a4d..57751a73 100644 --- a/typed/symdiff_test.go +++ b/typed/symdiff_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) type symdiffTestCase struct { diff --git a/typed/tofieldset.go b/typed/tofieldset.go index 015f1110..047efff0 100644 --- a/typed/tofieldset.go +++ b/typed/tofieldset.go @@ -19,9 +19,9 @@ package typed import ( "sync" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) var tPool = sync.Pool{ diff --git a/typed/toset_test.go b/typed/toset_test.go index 554ba901..2cc6810c 100644 --- a/typed/toset_test.go +++ b/typed/toset_test.go @@ -20,9 +20,9 @@ import ( "fmt" "testing" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/typed" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) type objSetPair struct { diff --git a/typed/typed.go b/typed/typed.go index b66fccb2..d63a97fe 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -21,9 +21,9 @@ import ( "strings" "sync" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // AsTyped accepts a value and a type and returns a TypedValue. 'v' must have diff --git a/typed/union.go b/typed/union.go index ee04bf22..1fa5d88a 100644 --- a/typed/union.go +++ b/typed/union.go @@ -20,8 +20,8 @@ import ( "fmt" "strings" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) func normalizeUnions(w *mergingWalker) error { diff --git a/typed/union_test.go b/typed/union_test.go index 09f7335d..a3ed129a 100644 --- a/typed/union_test.go +++ b/typed/union_test.go @@ -19,7 +19,7 @@ package typed_test import ( "testing" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) var unionParser = func() typed.ParseableType { diff --git a/typed/validate.go b/typed/validate.go index b7332fc7..378d3021 100644 --- a/typed/validate.go +++ b/typed/validate.go @@ -19,9 +19,9 @@ package typed import ( "sync" - "sigs.k8s.io/structured-merge-diff/v5/fieldpath" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/value" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" ) var vPool = sync.Pool{ diff --git a/typed/validate_test.go b/typed/validate_test.go index ac54e4c8..43357c5c 100644 --- a/typed/validate_test.go +++ b/typed/validate_test.go @@ -21,8 +21,8 @@ import ( "strings" "testing" - "sigs.k8s.io/structured-merge-diff/v5/schema" - "sigs.k8s.io/structured-merge-diff/v5/typed" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/typed" ) type validationTestCase struct { From b4e893ee12dcb7e478494cb78b8fd1c12f0bfe06 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 1 Aug 2022 16:45:55 -0700 Subject: [PATCH 028/105] Revert "use pointer semantics for schema" This reverts commit 60b3b658b1164439193c7f1199c63c96c051cbe5. --- internal/fixture/state.go | 2 +- schema/elements.go | 16 ++++++++++++---- schema/elements_test.go | 4 ++-- schema/equals_test.go | 12 ++++++------ typed/parser.go | 8 +++----- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/internal/fixture/state.go b/internal/fixture/state.go index b37a5e3b..37847858 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -477,7 +477,7 @@ func (cs ChangeParser) run(state *State) error { // Swap the schema in for use with the live object so it merges. // If the schema is incompatible, this will fail validation. - liveWithNewSchema, err := typed.AsTyped(state.Live.AsValue(), cs.Parser.Schema, state.Live.TypeRef()) + liveWithNewSchema, err := typed.AsTyped(state.Live.AsValue(), &cs.Parser.Schema, state.Live.TypeRef()) if err != nil { return err } diff --git a/schema/elements.go b/schema/elements.go index 34b504df..f6175374 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -30,7 +30,11 @@ type Schema struct { once sync.Once m map[string]TypeDef - lock sync.Mutex + // Once used to protect the initialization of `lock` field. + lockOnce sync.Once + // Lock which protects writes to resolvedTypes. Used as pointer so that + // schema may be used as a value type + lock *sync.Mutex // Cached results of resolving type references to atoms. Only stores // type references which require fields of Atom to be overriden. resolvedTypes map[TypeRef]Atom @@ -39,7 +43,7 @@ type Schema struct { // A TypeSpecifier references a particular type in a schema. type TypeSpecifier struct { Type TypeRef `yaml:"type,omitempty"` - Schema *Schema `yaml:"schema,omitempty"` + Schema Schema `yaml:"schema,omitempty"` } // TypeDef represents a named type in a schema. @@ -286,9 +290,13 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { } // Check to see if we have a cached version of this type + s.lockOnce.Do(func() { + s.lock = &sync.Mutex{} + s.resolvedTypes = make(map[TypeRef]Atom) + }) + s.lock.Lock() defer s.lock.Unlock() - s.resolvedTypes = make(map[TypeRef]Atom) var result Atom var exists bool @@ -327,7 +335,7 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { // If other is nil this method does nothing. // If other is already initialized, overwrites it with this instance // Warning: Not thread safe -func (s *Schema) CopyInto(dst *Schema) { +func (s Schema) CopyInto(dst *Schema) { if dst == nil { return } diff --git a/schema/elements_test.go b/schema/elements_test.go index be802fbf..1f05441f 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -161,7 +161,7 @@ func TestCopyInto(t *testing.T) { theCopy := Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(&s, &theCopy) { + if !reflect.DeepEqual(s, theCopy) { t.Fatal("") } @@ -170,7 +170,7 @@ func TestCopyInto(t *testing.T) { theCopy = Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(&s, &theCopy) { + if !reflect.DeepEqual(s, theCopy) { t.Fatal("") } }) diff --git a/schema/equals_test.go b/schema/equals_test.go index 0994a771..fff292c2 100644 --- a/schema/equals_test.go +++ b/schema/equals_test.go @@ -31,10 +31,10 @@ func fuzzInterface(i *interface{}, c fuzz.Continue) { *i = &m } -func (*Schema) Generate(rand *rand.Rand, size int) reflect.Value { - s := &Schema{} +func (Schema) Generate(rand *rand.Rand, size int) reflect.Value { + s := Schema{} f := fuzz.New().RandSource(rand).MaxDepth(4) - f.Fuzz(s) + f.Fuzz(&s) return reflect.ValueOf(s) } @@ -73,13 +73,13 @@ func TestEquals(t *testing.T) { // The "copy known fields" section of these function is to break if folks // add new fields without fixing the Equals function and this test. funcs := []interface{}{ - func(x *Schema) bool { - if !x.Equals(x) { + func(x Schema) bool { + if !x.Equals(&x) { return false } var y Schema y.Types = x.Types - return x.Equals(&y) == reflect.DeepEqual(x, &y) + return x.Equals(&y) == reflect.DeepEqual(&x, &y) }, func(x TypeDef) bool { if !x.Equals(&x) { diff --git a/typed/parser.go b/typed/parser.go index 1e657ef7..3949a78f 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -29,14 +29,12 @@ type YAMLObject string // Parser implements YAMLParser and allows introspecting the schema. type Parser struct { - Schema *schema.Schema + Schema schema.Schema } // create builds an unvalidated parser. func create(s YAMLObject) (*Parser, error) { - p := Parser{ - Schema: &schema.Schema{}, - } + p := Parser{} err := yaml.Unmarshal([]byte(s), &p.Schema) return &p, err } @@ -76,7 +74,7 @@ func (p *Parser) TypeNames() (names []string) { // errors are deferred until a further function is called. func (p *Parser) Type(name string) ParseableType { return ParseableType{ - Schema: p.Schema, + Schema: &p.Schema, TypeRef: schema.TypeRef{NamedType: &name}, } } From 4d3633c6fc7858f1c4eba4bde7243302c3176c4c Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 1 Aug 2022 17:35:01 -0700 Subject: [PATCH 029/105] Remove all copies of Schema and Map --- schema/elements.go | 46 +++++++++++++++++++++++++++++------------ schema/elements_test.go | 4 ++-- schema/equals_test.go | 20 +++++++++--------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/schema/elements.go b/schema/elements.go index f6175374..7e5dc758 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -30,11 +30,7 @@ type Schema struct { once sync.Once m map[string]TypeDef - // Once used to protect the initialization of `lock` field. - lockOnce sync.Once - // Lock which protects writes to resolvedTypes. Used as pointer so that - // schema may be used as a value type - lock *sync.Mutex + lock sync.Mutex // Cached results of resolving type references to atoms. Only stores // type references which require fields of Atom to be overriden. resolvedTypes map[TypeRef]Atom @@ -163,6 +159,31 @@ func (m *Map) FindField(name string) (StructField, bool) { return sf, ok } +// CopyInto this instance of Map into the other +// If other is nil this method does nothing. +// If other is already initialized, overwrites it with this instance +// Warning: Not thread safe +func (m *Map) CopyInto(dst *Map) { + if dst == nil { + return + } + + // Map type is considered immutable so sharing references + dst.Fields = m.Fields + dst.ElementType = m.ElementType + dst.Unions = m.Unions + dst.ElementRelationship = m.ElementRelationship + + if m.m != nil { + // If cache is non-nil then the once token had been consumed. + // Must reset token and use it again to ensure same semantics. + dst.once = sync.Once{} + dst.once.Do(func() { + dst.m = m.m + }) + } +} + // UnionFields are mapping between the fields that are part of the union and // their discriminated value. The discriminated value has to be set, and // should not conflict with other discriminated value in the list. @@ -289,15 +310,13 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { return s.resolveNoOverrides(tr) } - // Check to see if we have a cached version of this type - s.lockOnce.Do(func() { - s.lock = &sync.Mutex{} - s.resolvedTypes = make(map[TypeRef]Atom) - }) - s.lock.Lock() defer s.lock.Unlock() + if s.resolvedTypes == nil { + s.resolvedTypes = make(map[TypeRef]Atom) + } + var result Atom var exists bool @@ -308,7 +327,8 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { // Allow field-level electives to override the referred type's modifiers switch { case result.Map != nil: - mapCopy := *result.Map + mapCopy := Map{} + result.Map.CopyInto(&mapCopy) mapCopy.ElementRelationship = *tr.ElementRelationship result.Map = &mapCopy case result.List != nil: @@ -335,7 +355,7 @@ func (s *Schema) Resolve(tr TypeRef) (Atom, bool) { // If other is nil this method does nothing. // If other is already initialized, overwrites it with this instance // Warning: Not thread safe -func (s Schema) CopyInto(dst *Schema) { +func (s *Schema) CopyInto(dst *Schema) { if dst == nil { return } diff --git a/schema/elements_test.go b/schema/elements_test.go index 1f05441f..be802fbf 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -161,7 +161,7 @@ func TestCopyInto(t *testing.T) { theCopy := Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(s, theCopy) { + if !reflect.DeepEqual(&s, &theCopy) { t.Fatal("") } @@ -170,7 +170,7 @@ func TestCopyInto(t *testing.T) { theCopy = Schema{} s.CopyInto(&theCopy) - if !reflect.DeepEqual(s, theCopy) { + if !reflect.DeepEqual(&s, &theCopy) { t.Fatal("") } }) diff --git a/schema/equals_test.go b/schema/equals_test.go index fff292c2..377c35c2 100644 --- a/schema/equals_test.go +++ b/schema/equals_test.go @@ -31,18 +31,18 @@ func fuzzInterface(i *interface{}, c fuzz.Continue) { *i = &m } -func (Schema) Generate(rand *rand.Rand, size int) reflect.Value { +func (*Schema) Generate(rand *rand.Rand, size int) reflect.Value { s := Schema{} f := fuzz.New().RandSource(rand).MaxDepth(4) f.Fuzz(&s) - return reflect.ValueOf(s) + return reflect.ValueOf(&s) } -func (Map) Generate(rand *rand.Rand, size int) reflect.Value { +func (*Map) Generate(rand *rand.Rand, size int) reflect.Value { m := Map{} f := fuzz.New().RandSource(rand).MaxDepth(4).Funcs(fuzzInterface) f.Fuzz(&m) - return reflect.ValueOf(m) + return reflect.ValueOf(&m) } func (TypeDef) Generate(rand *rand.Rand, size int) reflect.Value { @@ -73,13 +73,13 @@ func TestEquals(t *testing.T) { // The "copy known fields" section of these function is to break if folks // add new fields without fixing the Equals function and this test. funcs := []interface{}{ - func(x Schema) bool { - if !x.Equals(&x) { + func(x *Schema) bool { + if !x.Equals(x) { return false } var y Schema y.Types = x.Types - return x.Equals(&y) == reflect.DeepEqual(&x, &y) + return x.Equals(&y) == reflect.DeepEqual(x, &y) }, func(x TypeDef) bool { if !x.Equals(&x) { @@ -109,8 +109,8 @@ func TestEquals(t *testing.T) { y.Map = x.Map return x.Equals(&y) == reflect.DeepEqual(x, y) }, - func(x Map) bool { - if !x.Equals(&x) { + func(x *Map) bool { + if !x.Equals(x) { return false } var y Map @@ -118,7 +118,7 @@ func TestEquals(t *testing.T) { y.ElementRelationship = x.ElementRelationship y.Fields = x.Fields y.Unions = x.Unions - return x.Equals(&y) == reflect.DeepEqual(&x, &y) + return x.Equals(&y) == reflect.DeepEqual(x, &y) }, func(x Union) bool { if !x.Equals(&x) { From 3f0a19a8e0d5a92b8ba210049387ac807f3477c7 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 10 Jun 2022 10:51:51 -0700 Subject: [PATCH 030/105] Add test to show that typename and version don't always match The test is flaky since the behavior depends on the order of which the versions are processed, and the current code that prevents that from happening depends on an invalid name for the version. --- merge/multiple_appliers_test.go | 155 ++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index 9ebcc662..8bdd716a 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -1945,3 +1945,158 @@ func TestMultipleAppliersReliantFieldsConversions(t *testing.T) { }) } } + +var versionDoesntMatchTypeName = func() Parser { + parser, err := typed.NewParser(`types: +- name: TypeV1 + map: + fields: + - name: field_foo_rely_on_bar + type: + scalar: string + - name: common_field + type: + scalar: string +- name: TypeV2 + map: + fields: + - name: required_field_bar + type: + scalar: string + - name: common_field + type: + scalar: string +`) + if err != nil { + panic(err) + } + return parser +}() + +type versionDoesntMatchTypenameConverter struct{} + +var _ merge.Converter = versionDoesntMatchTypenameConverter{} + +func (r versionDoesntMatchTypenameConverter) Convert(v *typed.TypedValue, version fieldpath.APIVersion) (*typed.TypedValue, error) { + inVersion := fieldpath.APIVersion("") + switch fieldpath.APIVersion(*v.TypeRef().NamedType) { + case "TypeV1": + inVersion = "v1" + case "TypeV2": + inVersion = "v2" + default: + return nil, fmt.Errorf(`Invalid typename: %q, should be one of ["TypeV1", "TypeV2"]`, version) + } + if inVersion == version { + return v, nil + } + if inVersion == version { + return v, nil + } + y, err := yaml.Marshal(v.AsValue().Unstructured()) + if err != nil { + return nil, err + } + inStr := string(y) + var outStr string + var outType string + switch version { + case "v1": + if !strings.Contains(inStr, "required_field_bar") { + return v, fmt.Errorf("missing requried field bar") + } + outType = "TypeV1" + outStr = strings.Replace(inStr, "required_field_bar", "field_foo_rely_on_bar", -1) + case "v2": + outType = "TypeV2" + outStr = strings.Replace(inStr, "field_foo_rely_on_bar", "required_field_bar", -1) + default: + return nil, missingVersionError + } + return versionDoesntMatchTypeName.Type(string(outType)).FromYAML(typed.YAMLObject(outStr)) +} + +func (r versionDoesntMatchTypenameConverter) IsMissingVersionError(err error) bool { + return err == missingVersionError +} + +// This is the same test as TestMultipleAppliersReliantFieldsConversions +// but written without the internal test framework that assumes that +// typenames and versions match. The goal of this test is to make sure +// that no such assumptions are used in the tested code. +func TestVersionDoesntMatchTypename(t *testing.T) { + converter := versionDoesntMatchTypenameConverter{} + updater := &merge.Updater{Converter: converter} + + // Apply in one version, apply in another, apply in a third. + live, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{}`) + if err != nil { + t.Fatalf("Failed to parse empty object: %v", err) + } + managers := fieldpath.ManagedFields{} + config, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "a"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } + + live, err = converter.Convert(live, "v1") + if err != nil { + t.Fatalf("Failed to convert object to v1: %v", err) + } + config, err = versionDoesntMatchTypeName.Type("TypeV1").FromYAML(`{"common_field": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + + live, managers, err = updater.Apply(live, config, "v1", managers, "v1_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } + + live, err = converter.Convert(live, "v2") + if err != nil { + t.Fatalf("Failed to convert object to v1: %v", err) + } + config, err = versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } + + expectedObject, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b", "common_field": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + if comparison, err := live.Compare(expectedObject); err != nil { + t.Fatalf("Failed to compare live with expected: %v", err) + } else if !comparison.IsSame() { + t.Fatalf("Live is different from expected:\n%v", comparison) + } + + expectedManagers := fieldpath.ManagedFields{ + "v2_applier": fieldpath.NewVersionedSet( + _NS( + _P("required_field_bar"), + ), + "v2", + true, + ), + "v1_applier": fieldpath.NewVersionedSet( + _NS( + _P("common_field"), + ), + "v1", + true, + ), + } + if !expectedManagers.Equals(managers) { + t.Fatalf("ManagedFields not as expected:\n%v", managers) + } +} From 711abd75deaeec5b672f3c86ce27fcfb32a76f6c Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 10 Jun 2022 19:52:35 -0700 Subject: [PATCH 031/105] Run the test 10 times in a row since it is flaky --- merge/multiple_appliers_test.go | 128 ++++++++++++++++---------------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index 8bdd716a..01ce32e0 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -2028,75 +2028,77 @@ func TestVersionDoesntMatchTypename(t *testing.T) { converter := versionDoesntMatchTypenameConverter{} updater := &merge.Updater{Converter: converter} - // Apply in one version, apply in another, apply in a third. - live, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{}`) - if err != nil { - t.Fatalf("Failed to parse empty object: %v", err) - } - managers := fieldpath.ManagedFields{} - config, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "a"}`) - if err != nil { - t.Fatalf("Failed to parse object: %v", err) - } - live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) - if err != nil { - t.Fatalf("Failed to apply: %v", err) - } + for i := 0; i < 10; i++ { + // Apply in one version, apply in another, apply in a third. + live, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{}`) + if err != nil { + t.Fatalf("Failed to parse empty object: %v", err) + } + managers := fieldpath.ManagedFields{} + config, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "a"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } - live, err = converter.Convert(live, "v1") - if err != nil { - t.Fatalf("Failed to convert object to v1: %v", err) - } - config, err = versionDoesntMatchTypeName.Type("TypeV1").FromYAML(`{"common_field": "b"}`) - if err != nil { - t.Fatalf("Failed to parse object: %v", err) - } + live, err = converter.Convert(live, "v1") + if err != nil { + t.Fatalf("Failed to convert object to v1: %v", err) + } + config, err = versionDoesntMatchTypeName.Type("TypeV1").FromYAML(`{"common_field": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } - live, managers, err = updater.Apply(live, config, "v1", managers, "v1_applier", false) - if err != nil { - t.Fatalf("Failed to apply: %v", err) - } + live, managers, err = updater.Apply(live, config, "v1", managers, "v1_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } - live, err = converter.Convert(live, "v2") - if err != nil { - t.Fatalf("Failed to convert object to v1: %v", err) - } - config, err = versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b"}`) - if err != nil { - t.Fatalf("Failed to parse object: %v", err) - } - live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) - if err != nil { - t.Fatalf("Failed to apply: %v", err) - } + live, err = converter.Convert(live, "v2") + if err != nil { + t.Fatalf("Failed to convert object to v1: %v", err) + } + config, err = versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + live, managers, err = updater.Apply(live, config, "v2", managers, "v2_applier", false) + if err != nil { + t.Fatalf("Failed to apply: %v", err) + } - expectedObject, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b", "common_field": "b"}`) - if err != nil { - t.Fatalf("Failed to parse object: %v", err) - } - if comparison, err := live.Compare(expectedObject); err != nil { - t.Fatalf("Failed to compare live with expected: %v", err) - } else if !comparison.IsSame() { - t.Fatalf("Live is different from expected:\n%v", comparison) - } + expectedObject, err := versionDoesntMatchTypeName.Type("TypeV2").FromYAML(`{"required_field_bar": "b", "common_field": "b"}`) + if err != nil { + t.Fatalf("Failed to parse object: %v", err) + } + if comparison, err := live.Compare(expectedObject); err != nil { + t.Fatalf("Failed to compare live with expected: %v", err) + } else if !comparison.IsSame() { + t.Fatalf("Live is different from expected:\n%v", comparison) + } - expectedManagers := fieldpath.ManagedFields{ - "v2_applier": fieldpath.NewVersionedSet( - _NS( - _P("required_field_bar"), + expectedManagers := fieldpath.ManagedFields{ + "v2_applier": fieldpath.NewVersionedSet( + _NS( + _P("required_field_bar"), + ), + "v2", + true, ), - "v2", - true, - ), - "v1_applier": fieldpath.NewVersionedSet( - _NS( - _P("common_field"), + "v1_applier": fieldpath.NewVersionedSet( + _NS( + _P("common_field"), + ), + "v1", + true, ), - "v1", - true, - ), - } - if !expectedManagers.Equals(managers) { - t.Fatalf("ManagedFields not as expected:\n%v", managers) + } + if !expectedManagers.Equals(managers) { + t.Fatalf("ManagedFields not as expected:\n%v", managers) + } } } From f76ddd22c381af44a6e18586dad88d12f47c2707 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 10 Jun 2022 19:55:26 -0700 Subject: [PATCH 032/105] Use version of the object rather than from typename --- merge/update.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/merge/update.go b/merge/update.go index 1b23dcbd..95ab2e78 100644 --- a/merge/update.go +++ b/merge/update.go @@ -218,7 +218,8 @@ func (s *Updater) prune(merged *typed.TypedValue, managers fieldpath.ManagedFiel if lastSet == nil || lastSet.Set().Empty() { return merged, nil } - convertedMerged, err := s.Converter.Convert(merged, lastSet.APIVersion()) + version := lastSet.APIVersion() + convertedMerged, err := s.Converter.Convert(merged, version) if err != nil { if s.Converter.IsMissingVersionError(err) { return merged, nil @@ -228,7 +229,7 @@ func (s *Updater) prune(merged *typed.TypedValue, managers fieldpath.ManagedFiel sc, tr := convertedMerged.Schema(), convertedMerged.TypeRef() pruned := convertedMerged.RemoveItems(lastSet.Set().EnsureNamedFieldsAreMembers(sc, tr)) - pruned, err = s.addBackOwnedItems(convertedMerged, pruned, managers, applyingManager) + pruned, err = s.addBackOwnedItems(convertedMerged, pruned, version, managers, applyingManager) if err != nil { return nil, fmt.Errorf("failed add back owned items: %v", err) } @@ -241,7 +242,7 @@ func (s *Updater) prune(merged *typed.TypedValue, managers fieldpath.ManagedFiel // addBackOwnedItems adds back any fields, list and map items that were removed by prune, // but other appliers or updaters (or the current applier's new config) claim to own. -func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFields fieldpath.ManagedFields, applyingManager string) (*typed.TypedValue, error) { +func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, prunedVersion fieldpath.APIVersion, managedFields fieldpath.ManagedFields, applyingManager string) (*typed.TypedValue, error) { var err error managedAtVersion := map[fieldpath.APIVersion]*fieldpath.Set{} for _, managerSet := range managedFields { @@ -252,7 +253,6 @@ func (s *Updater) addBackOwnedItems(merged, pruned *typed.TypedValue, managedFie } // Add back owned items at pruned version first to avoid conversion failure // caused by pruned fields which are required for conversion. - prunedVersion := fieldpath.APIVersion(*pruned.TypeRef().NamedType) if managed, ok := managedAtVersion[prunedVersion]; ok { merged, pruned, err = s.addBackOwnedItemsForVersion(merged, pruned, prunedVersion, managed) if err != nil { From 3726c8044c0fa5eb4767f144019fee5df4e0bd61 Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Wed, 21 Dec 2022 22:18:39 +0300 Subject: [PATCH 033/105] Check inlined struct field's kind before recursively calling value.buildStructCacheEntry Signed-off-by: Alper Rifat Ulucinar --- value/reflectcache.go | 4 ++- value/reflectcache_test.go | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/value/reflectcache.go b/value/reflectcache.go index a5a467c0..f0d58d42 100644 --- a/value/reflectcache.go +++ b/value/reflectcache.go @@ -154,7 +154,9 @@ func buildStructCacheEntry(t reflect.Type, infos map[string]*FieldCacheEntry, fi if field.Type.Kind() == reflect.Ptr { e = field.Type.Elem() } - buildStructCacheEntry(e, infos, append(fieldPath, field.Index)) + if e.Kind() == reflect.Struct { + buildStructCacheEntry(e, infos, append(fieldPath, field.Index)) + } continue } info := &FieldCacheEntry{JsonName: jsonName, isOmitEmpty: isOmitempty, fieldPath: append(fieldPath, field.Index), fieldType: field.Type} diff --git a/value/reflectcache_test.go b/value/reflectcache_test.go index 9f54e0b0..523e44a0 100644 --- a/value/reflectcache_test.go +++ b/value/reflectcache_test.go @@ -77,3 +77,76 @@ func TestToUnstructured(t *testing.T) { }) } } + +func TestTypeReflectEntryOf(t *testing.T) { + testString := "" + tests := map[string]struct { + arg interface{} + want *TypeReflectCacheEntry + }{ + "StructWithStringField": { + arg: struct { + F1 string `json:"f1"` + }{}, + want: &TypeReflectCacheEntry{ + structFields: map[string]*FieldCacheEntry{ + "f1": { + JsonName: "f1", + fieldPath: [][]int{{0}}, + fieldType: reflect.TypeOf(testString), + TypeEntry: &TypeReflectCacheEntry{}, + }, + }, + orderedStructFields: []*FieldCacheEntry{ + { + JsonName: "f1", + fieldPath: [][]int{{0}}, + fieldType: reflect.TypeOf(testString), + TypeEntry: &TypeReflectCacheEntry{}, + }, + }, + }, + }, + "StructWith*StringFieldOmitempty": { + arg: struct { + F1 *string `json:"f1,omitempty"` + }{}, + want: &TypeReflectCacheEntry{ + structFields: map[string]*FieldCacheEntry{ + "f1": { + JsonName: "f1", + isOmitEmpty: true, + fieldPath: [][]int{{0}}, + fieldType: reflect.TypeOf(&testString), + TypeEntry: &TypeReflectCacheEntry{}, + }, + }, + orderedStructFields: []*FieldCacheEntry{ + { + JsonName: "f1", + isOmitEmpty: true, + fieldPath: [][]int{{0}}, + fieldType: reflect.TypeOf(&testString), + TypeEntry: &TypeReflectCacheEntry{}, + }, + }, + }, + }, + "StructWithInlinedField": { + arg: struct { + F1 string `json:",inline"` + }{}, + want: &TypeReflectCacheEntry{ + structFields: map[string]*FieldCacheEntry{}, + orderedStructFields: []*FieldCacheEntry{}, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if got := TypeReflectEntryOf(reflect.TypeOf(tt.arg)); !reflect.DeepEqual(got, tt.want) { + t.Errorf("TypeReflectEntryOf() = %v, want %v", got, tt.want) + } + }) + } +} From 7cc47843ea192bff262f6b286197c13e49fbd980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mudrini=C4=87?= Date: Mon, 6 Mar 2023 16:37:33 +0100 Subject: [PATCH 034/105] Replace k8s.gcr.io with registry.k8s.io MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marko Mudrinić --- internal/testdata/k8s-deployment.yaml | 6 +-- internal/testdata/node.yaml | 66 +++++++++++++-------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/internal/testdata/k8s-deployment.yaml b/internal/testdata/k8s-deployment.yaml index 899ca327..ddacf2e4 100644 --- a/internal/testdata/k8s-deployment.yaml +++ b/internal/testdata/k8s-deployment.yaml @@ -38,7 +38,7 @@ spec: optional: true containers: - name: kubedns - image: k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.10 + image: registry.k8s.io/k8s-dns-kube-dns-amd64:1.14.10 resources: # TODO: Set memory limits when we've profiled the container for large # clusters, then set request = limit to keep this container in @@ -89,7 +89,7 @@ spec: - name: kube-dns-config mountPath: /kube-dns-config - name: dnsmasq - image: k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64:1.14.10 + image: registry.k8s.io/k8s-dns-dnsmasq-nanny-amd64:1.14.10 livenessProbe: httpGet: path: /healthcheck/dnsmasq @@ -128,7 +128,7 @@ spec: - name: kube-dns-config mountPath: /etc/k8s/dns/dnsmasq-nanny - name: sidecar - image: k8s.gcr.io/k8s-dns-sidecar-amd64:1.14.10 + image: registry.k8s.io/k8s-dns-sidecar-amd64:1.14.10 livenessProbe: httpGet: path: /metrics diff --git a/internal/testdata/node.yaml b/internal/testdata/node.yaml index dd8681ae..b6e8aeb9 100644 --- a/internal/testdata/node.yaml +++ b/internal/testdata/node.yaml @@ -131,8 +131,8 @@ status: - grafana/grafana:4.4.2 sizeBytes: 287008013 - names: - - k8s.gcr.io/node-problem-detector@sha256:f95cab985c26b2f46e9bd43283e0bfa88860c14e0fb0649266babe8b65e9eb2b - - k8s.gcr.io/node-problem-detector:v0.4.1 + - registry.k8s.io/node-problem-detector@sha256:f95cab985c26b2f46e9bd43283e0bfa88860c14e0fb0649266babe8b65e9eb2b + - registry.k8s.io/node-problem-detector:v0.4.1 sizeBytes: 286572743 - names: - grafana/grafana@sha256:7ff7f9b2501a5d55b55ce3f58d21771b1c5af1f2a4ab7dbf11bef7142aae7033 @@ -151,76 +151,76 @@ status: - nginx:1.10.1 sizeBytes: 180708613 - names: - - k8s.gcr.io/fluentd-elasticsearch@sha256:b8c94527b489fb61d3d81ce5ad7f3ddbb7be71e9620a3a36e2bede2f2e487d73 - - k8s.gcr.io/fluentd-elasticsearch:v2.0.4 + - registry.k8s.io/fluentd-elasticsearch@sha256:b8c94527b489fb61d3d81ce5ad7f3ddbb7be71e9620a3a36e2bede2f2e487d73 + - registry.k8s.io/fluentd-elasticsearch:v2.0.4 sizeBytes: 135716379 - names: - nginx@sha256:00be67d6ba53d5318cd91c57771530f5251cfbe028b7be2c4b70526f988cfc9f - nginx:latest sizeBytes: 109357355 - names: - - k8s.gcr.io/kubernetes-dashboard-amd64@sha256:dc4026c1b595435ef5527ca598e1e9c4343076926d7d62b365c44831395adbd0 - - k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3 + - registry.k8s.io/kubernetes-dashboard-amd64@sha256:dc4026c1b595435ef5527ca598e1e9c4343076926d7d62b365c44831395adbd0 + - registry.k8s.io/kubernetes-dashboard-amd64:v1.8.3 sizeBytes: 102319441 - names: - gcr.io/google_containers/kube-proxy:v1.11.10-gke.5 - - k8s.gcr.io/kube-proxy:v1.11.10-gke.5 + - registry.k8s.io/kube-proxy:v1.11.10-gke.5 sizeBytes: 102279340 - names: - - k8s.gcr.io/event-exporter@sha256:7f9cd7cb04d6959b0aa960727d04fa86759008048c785397b7b0d9dff0007516 - - k8s.gcr.io/event-exporter:v0.2.3 + - registry.k8s.io/event-exporter@sha256:7f9cd7cb04d6959b0aa960727d04fa86759008048c785397b7b0d9dff0007516 + - registry.k8s.io/event-exporter:v0.2.3 sizeBytes: 94171943 - names: - - k8s.gcr.io/prometheus-to-sd@sha256:6c0c742475363d537ff059136e5d5e4ab1f512ee0fd9b7ca42ea48bc309d1662 - - k8s.gcr.io/prometheus-to-sd:v0.3.1 + - registry.k8s.io/prometheus-to-sd@sha256:6c0c742475363d537ff059136e5d5e4ab1f512ee0fd9b7ca42ea48bc309d1662 + - registry.k8s.io/prometheus-to-sd:v0.3.1 sizeBytes: 88077694 - names: - - k8s.gcr.io/fluentd-gcp-scaler@sha256:a5ace7506d393c4ed65eb2cbb6312c64ab357fcea16dff76b9055bc6e498e5ff - - k8s.gcr.io/fluentd-gcp-scaler:0.5.1 + - registry.k8s.io/fluentd-gcp-scaler@sha256:a5ace7506d393c4ed65eb2cbb6312c64ab357fcea16dff76b9055bc6e498e5ff + - registry.k8s.io/fluentd-gcp-scaler:0.5.1 sizeBytes: 86637208 - names: - - k8s.gcr.io/heapster-amd64@sha256:9fae0af136ce0cf4f88393b3670f7139ffc464692060c374d2ae748e13144521 - - k8s.gcr.io/heapster-amd64:v1.6.0-beta.1 + - registry.k8s.io/heapster-amd64@sha256:9fae0af136ce0cf4f88393b3670f7139ffc464692060c374d2ae748e13144521 + - registry.k8s.io/heapster-amd64:v1.6.0-beta.1 sizeBytes: 76016169 - names: - - k8s.gcr.io/ingress-glbc-amd64@sha256:31d36bbd9c44caffa135fc78cf0737266fcf25e3cf0cd1c2fcbfbc4f7309cc52 - - k8s.gcr.io/ingress-glbc-amd64:v1.1.1 + - registry.k8s.io/ingress-glbc-amd64@sha256:31d36bbd9c44caffa135fc78cf0737266fcf25e3cf0cd1c2fcbfbc4f7309cc52 + - registry.k8s.io/ingress-glbc-amd64:v1.1.1 sizeBytes: 67801919 - names: - - k8s.gcr.io/kube-addon-manager@sha256:d53486c3a0b49ebee019932878dc44232735d5622a51dbbdcec7124199020d09 - - k8s.gcr.io/kube-addon-manager:v8.7 + - registry.k8s.io/kube-addon-manager@sha256:d53486c3a0b49ebee019932878dc44232735d5622a51dbbdcec7124199020d09 + - registry.k8s.io/kube-addon-manager:v8.7 sizeBytes: 63322109 - names: - nginx@sha256:4aacdcf186934dcb02f642579314075910f1855590fd3039d8fa4c9f96e48315 - nginx:1.10-alpine sizeBytes: 54042627 - names: - - k8s.gcr.io/cpvpa-amd64@sha256:cfe7b0a11c9c8e18c87b1eb34fef9a7cbb8480a8da11fc2657f78dbf4739f869 - - k8s.gcr.io/cpvpa-amd64:v0.6.0 + - registry.k8s.io/cpvpa-amd64@sha256:cfe7b0a11c9c8e18c87b1eb34fef9a7cbb8480a8da11fc2657f78dbf4739f869 + - registry.k8s.io/cpvpa-amd64:v0.6.0 sizeBytes: 51785854 - names: - - k8s.gcr.io/cluster-proportional-autoscaler-amd64@sha256:003f98d9f411ddfa6ff6d539196355e03ddd69fa4ed38c7ffb8fec6f729afe2d - - k8s.gcr.io/cluster-proportional-autoscaler-amd64:1.1.2-r2 + - registry.k8s.io/cluster-proportional-autoscaler-amd64@sha256:003f98d9f411ddfa6ff6d539196355e03ddd69fa4ed38c7ffb8fec6f729afe2d + - registry.k8s.io/cluster-proportional-autoscaler-amd64:1.1.2-r2 sizeBytes: 49648481 - names: - - k8s.gcr.io/ip-masq-agent-amd64@sha256:1ffda57d87901bc01324c82ceb2145fe6a0448d3f0dd9cb65aa76a867cd62103 - - k8s.gcr.io/ip-masq-agent-amd64:v2.1.1 + - registry.k8s.io/ip-masq-agent-amd64@sha256:1ffda57d87901bc01324c82ceb2145fe6a0448d3f0dd9cb65aa76a867cd62103 + - registry.k8s.io/ip-masq-agent-amd64:v2.1.1 sizeBytes: 49612505 - names: - - k8s.gcr.io/k8s-dns-kube-dns-amd64@sha256:b99fc3eee2a9f052f7eb4cc00f15eb12fc405fa41019baa2d6b79847ae7284a8 - - k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.10 + - registry.k8s.io/k8s-dns-kube-dns-amd64@sha256:b99fc3eee2a9f052f7eb4cc00f15eb12fc405fa41019baa2d6b79847ae7284a8 + - registry.k8s.io/k8s-dns-kube-dns-amd64:1.14.10 sizeBytes: 49549457 - names: - - k8s.gcr.io/rescheduler@sha256:156cfbfd05a5a815206fd2eeb6cbdaf1596d71ea4b415d3a6c43071dd7b99450 - - k8s.gcr.io/rescheduler:v0.4.0 + - registry.k8s.io/rescheduler@sha256:156cfbfd05a5a815206fd2eeb6cbdaf1596d71ea4b415d3a6c43071dd7b99450 + - registry.k8s.io/rescheduler:v0.4.0 sizeBytes: 48973149 - names: - - k8s.gcr.io/event-exporter@sha256:16ca66e2b5dc7a1ce6a5aafcb21d0885828b75cdfc08135430480f7ad2364adc - - k8s.gcr.io/event-exporter:v0.2.4 + - registry.k8s.io/event-exporter@sha256:16ca66e2b5dc7a1ce6a5aafcb21d0885828b75cdfc08135430480f7ad2364adc + - registry.k8s.io/event-exporter:v0.2.4 sizeBytes: 47261019 - names: - - k8s.gcr.io/coredns@sha256:db2bf53126ed1c761d5a41f24a1b82a461c85f736ff6e90542e9522be4757848 - - k8s.gcr.io/coredns:1.1.3 + - registry.k8s.io/coredns@sha256:db2bf53126ed1c761d5a41f24a1b82a461c85f736ff6e90542e9522be4757848 + - registry.k8s.io/coredns:1.1.3 sizeBytes: 45587362 - names: - prom/prometheus@sha256:483f4c9d7733699ba79facca9f8bcce1cef1af43dfc3e7c5a1882aa85f53cb74 From 3440e0c4e77362e669b81e921cce0c0fe64ab6af Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Tue, 13 Dec 2022 16:00:00 -0800 Subject: [PATCH 035/105] schema: Fix unknown scalar types Also properly validate untyped schema types, and fail if unknown scalar types are present. --- merge/nested_test.go | 2 +- schema/elements.go | 3 ++- schema/schemaschema.go | 2 +- typed/merge.go | 11 ++++++----- typed/validate.go | 6 ++++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/merge/nested_test.go b/merge/nested_test.go index c87c46bb..eb65a5a9 100644 --- a/merge/nested_test.go +++ b/merge/nested_test.go @@ -55,7 +55,7 @@ var nestedTypeParser = func() Parser { scalar: string - name: value type: - scalar: number + scalar: numeric - name: listOfLists list: elementType: diff --git a/schema/elements.go b/schema/elements.go index 7e5dc758..5d3707a5 100644 --- a/schema/elements.go +++ b/schema/elements.go @@ -73,7 +73,7 @@ type Atom struct { } // Scalar (AKA "primitive") represents a type which has a single value which is -// either numeric, string, or boolean. +// either numeric, string, or boolean, or untyped for any of them. // // TODO: split numeric into float/int? Something even more fine-grained? type Scalar string @@ -82,6 +82,7 @@ const ( Numeric = Scalar("numeric") String = Scalar("string") Boolean = Scalar("boolean") + Untyped = Scalar("untyped") ) // ElementRelationship is an enum of the different possible relationships diff --git a/schema/schemaschema.go b/schema/schemaschema.go index 7d64d130..e4c5caa2 100644 --- a/schema/schemaschema.go +++ b/schema/schemaschema.go @@ -110,7 +110,7 @@ var SchemaSchemaYAML = `types: scalar: string - name: deduceInvalidDiscriminator type: - scalar: bool + scalar: boolean - name: fields type: list: diff --git a/typed/merge.go b/typed/merge.go index 91364408..09209ec8 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -113,11 +113,12 @@ func (w *mergingWalker) doLeaf() { w.rule(w) } -func (w *mergingWalker) doScalar(t *schema.Scalar) (errs ValidationErrors) { - errs = append(errs, validateScalar(t, w.lhs, "lhs: ")...) - errs = append(errs, validateScalar(t, w.rhs, "rhs: ")...) - if len(errs) > 0 { - return errs +func (w *mergingWalker) doScalar(t *schema.Scalar) ValidationErrors { + // Make sure at least one side is a valid scalar. + lerrs := validateScalar(t, w.lhs, "lhs: ") + rerrs := validateScalar(t, w.rhs, "rhs: ") + if len(lerrs) > 0 && len(rerrs) > 0 { + return append(lerrs, rerrs...) } // All scalars are leaf fields. diff --git a/typed/validate.go b/typed/validate.go index 378d3021..edddbafa 100644 --- a/typed/validate.go +++ b/typed/validate.go @@ -102,6 +102,12 @@ func validateScalar(t *schema.Scalar, v value.Value, prefix string) (errs Valida if !v.IsBool() { return errorf("%vexpected boolean, got %v", prefix, v) } + case schema.Untyped: + if !v.IsFloat() && !v.IsInt() && !v.IsString() && !v.IsBool() { + return errorf("%vexpected any scalar, got %v", prefix, v) + } + default: + return errorf("%vunexpected scalar type in schema: %v", prefix, *t) } return nil } From 71a41e1ab52d6e4362e55599b2b339e18aae19b2 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 6 Mar 2023 08:38:09 -0800 Subject: [PATCH 036/105] test: Do not capture loop variable --- schema/elements_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schema/elements_test.go b/schema/elements_test.go index be802fbf..b097b61b 100644 --- a/schema/elements_test.go +++ b/schema/elements_test.go @@ -151,7 +151,8 @@ func TestCopyInto(t *testing.T) { {"existingNamedType", []TypeDef{{Name: existing, Atom: a}}, TypeRef{NamedType: &existing}}, } - for _, tt := range tests { + for i := range tests { + tt := tests[i] t.Run(tt.testName, func(t *testing.T) { t.Parallel() s := Schema{ From 896115462f81d9402fd09dfe4b16b9470be19272 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 6 Mar 2023 13:27:09 -0800 Subject: [PATCH 037/105] Mark AsTypeUnvalidated as deprecated --- typed/typed.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/typed/typed.go b/typed/typed.go index d63a97fe..5071e865 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -45,6 +45,10 @@ func AsTyped(v value.Value, s *schema.Schema, typeRef schema.TypeRef) (*TypedVal // conforms to the schema, for cases where that has already been checked or // where you're going to call a method that validates as a side-effect (like // ToFieldSet). +// +// Deprecated: This function was initially created because validation +// was expensive. Now that this has been solved, objects should always +// be created as validated, using `AsTyped`. func AsTypedUnvalidated(v value.Value, s *schema.Schema, typeRef schema.TypeRef) *TypedValue { tv := &TypedValue{ value: v, From 04eae3ad39989b2b72ccbba5fe44a19b78a91f29 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 21 Jun 2023 14:27:24 -0700 Subject: [PATCH 038/105] fixture: Show diff when managers don't match --- internal/fixture/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 37847858..80d87ced 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -588,7 +588,7 @@ func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) e if tc.Managed != nil { if diff := state.Managers.Difference(tc.Managed); len(diff) != 0 { - return fmt.Errorf("expected Managers to be:\n%v\ngot:\n%v", tc.Managed, state.Managers) + return fmt.Errorf("expected Managers to be:\n%v\ngot:\n%v\ndiff:\n%v", tc.Managed, state.Managers, diff) } } From 758c177eace12b2e22466e3144bdd94422c873a4 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 26 Jun 2023 16:31:49 -0700 Subject: [PATCH 039/105] value: Add benchmark for value.Equals --- value/equals_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 value/equals_test.go diff --git a/value/equals_test.go b/value/equals_test.go new file mode 100644 index 00000000..93847a38 --- /dev/null +++ b/value/equals_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 value_test + +import ( + "io/ioutil" + "path/filepath" + "testing" + + "gopkg.in/yaml.v2" + "sigs.k8s.io/structured-merge-diff/v4/value" +) + +func testdata(file string) string { + return filepath.Join("..", "internal", "testdata", file) +} + +func read(file string) []byte { + s, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + return s +} + +func BenchmarkEquals(b *testing.B) { + benches := []struct { + filename string + }{ + { + filename: "pod.yaml", + }, + { + filename: "endpoints.yaml", + }, + { + filename: "list.yaml", + }, + { + filename: "node.yaml", + }, + { + filename: "prometheus-crd.yaml", + }, + } + + for _, bench := range benches { + b.Run(bench.filename, func(b *testing.B) { + var obj interface{} + err := yaml.Unmarshal(read(testdata(bench.filename)), &obj) + if err != nil { + b.Fatalf("Failed to unmarshal object: %v", err) + } + v := value.NewValueInterface(obj) + b.Run("Equals", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if !value.Equals(v, v) { + b.Fatalf("Object should be equal") + } + } + }) + b.Run("EqualsUsingFreelist", func(b *testing.B) { + b.ReportAllocs() + a := value.NewFreelistAllocator() + for i := 0; i < b.N; i++ { + if !value.EqualsUsing(a, v, v) { + b.Fatalf("Object should be equal") + } + } + }) + }) + } +} From 8bff4882d3f2d622609106c121c80451f5b11ae7 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 26 Jun 2023 14:36:33 -0700 Subject: [PATCH 040/105] Use EqualsUsing recursively MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, EqualsUsing recurses with Equals, which loses the allocator that you're using, defeating the whole point. Address that in all places I could find. Doesn't affect anything yet since it's not used beside the benchmark: ``` name old time/op new time/op delta Equals/pod.yaml/Equals-8 19.7µs ± 5% 19.3µs ± 2% ~ (p=0.222 n=5+5) Equals/pod.yaml/EqualsUsingFreelist-8 19.4µs ± 4% 16.5µs ± 3% -14.95% (p=0.008 n=5+5) Equals/endpoints.yaml/Equals-8 982µs ± 3% 977µs ± 5% ~ (p=0.548 n=5+5) Equals/endpoints.yaml/EqualsUsingFreelist-8 970µs ± 5% 872µs ± 6% -10.02% (p=0.008 n=5+5) Equals/list.yaml/Equals-8 4.02µs ± 1% 4.20µs ± 8% +4.37% (p=0.008 n=5+5) Equals/list.yaml/EqualsUsingFreelist-8 4.03µs ± 4% 3.42µs ± 4% -15.15% (p=0.008 n=5+5) Equals/node.yaml/Equals-8 42.0µs ± 3% 40.2µs ± 2% -4.33% (p=0.016 n=5+5) Equals/node.yaml/EqualsUsingFreelist-8 40.3µs ± 2% 34.4µs ± 5% -14.55% (p=0.008 n=5+5) Equals/prometheus-crd.yaml/Equals-8 340µs ± 2% 347µs ± 2% ~ (p=0.151 n=5+5) Equals/prometheus-crd.yaml/EqualsUsingFreelist-8 352µs ± 3% 301µs ± 9% -14.57% (p=0.008 n=5+5) name old alloc/op new alloc/op delta Equals/pod.yaml/Equals-8 3.59kB ± 0% 4.36kB ± 0% +21.38% (p=0.008 n=5+5) Equals/pod.yaml/EqualsUsingFreelist-8 3.58kB ± 0% 2.18kB ± 0% -38.93% (p=0.008 n=5+5) Equals/endpoints.yaml/Equals-8 113kB ± 0% 161kB ± 0% +42.63% (p=0.029 n=4+4) Equals/endpoints.yaml/EqualsUsingFreelist-8 113kB ± 0% 96kB ± 0% -14.53% (p=0.008 n=5+5) Equals/list.yaml/Equals-8 1.02kB ± 0% 1.28kB ± 0% +25.98% (p=0.008 n=5+5) Equals/list.yaml/EqualsUsingFreelist-8 1.00kB ± 0% 0.67kB ± 0% -32.80% (p=0.008 n=5+5) Equals/node.yaml/Equals-8 9.25kB ± 0% 10.64kB ± 0% +15.05% (p=0.008 n=5+5) Equals/node.yaml/EqualsUsingFreelist-8 9.23kB ± 0% 4.94kB ± 0% -46.45% (p=0.008 n=5+5) Equals/prometheus-crd.yaml/Equals-8 60.7kB ± 0% 79.9kB ± 0% +31.69% (p=0.016 n=4+5) Equals/prometheus-crd.yaml/EqualsUsingFreelist-8 60.6kB ± 0% 44.1kB ± 0% ~ (p=0.079 n=4+5) name old allocs/op new allocs/op delta Equals/pod.yaml/Equals-8 159 ± 0% 159 ± 0% ~ (all equal) Equals/pod.yaml/EqualsUsingFreelist-8 158 ± 0% 59 ± 0% -62.66% (p=0.008 n=5+5) Equals/endpoints.yaml/Equals-8 6.04k ± 0% 6.04k ± 0% ~ (all equal) Equals/endpoints.yaml/EqualsUsingFreelist-8 6.04k ± 0% 2.01k ± 0% -66.63% (p=0.008 n=5+5) Equals/list.yaml/Equals-8 47.0 ± 0% 47.0 ± 0% ~ (all equal) Equals/list.yaml/EqualsUsingFreelist-8 46.0 ± 0% 17.0 ± 0% -63.04% (p=0.008 n=5+5) Equals/node.yaml/Equals-8 384 ± 0% 384 ± 0% ~ (all equal) Equals/node.yaml/EqualsUsingFreelist-8 383 ± 0% 148 ± 0% -61.36% (p=0.008 n=5+5) Equals/prometheus-crd.yaml/Equals-8 2.96k ± 0% 2.96k ± 0% ~ (all equal) Equals/prometheus-crd.yaml/EqualsUsingFreelist-8 2.96k ± 0% 1.04k ± 0% -64.87% (p=0.008 n=5+5) ``` --- value/mapreflect.go | 2 +- value/mapunstructured.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/value/mapreflect.go b/value/mapreflect.go index dc8b8c72..c38402b9 100644 --- a/value/mapreflect.go +++ b/value/mapreflect.go @@ -136,7 +136,7 @@ func (r mapReflect) EqualsUsing(a Allocator, m Map) bool { if !ok { return false } - return Equals(vr.mustReuse(lhsVal, entry, nil, nil), value) + return EqualsUsing(a, vr.mustReuse(lhsVal, entry, nil, nil), value) }) } diff --git a/value/mapunstructured.go b/value/mapunstructured.go index d8e20862..c3ae00b1 100644 --- a/value/mapunstructured.go +++ b/value/mapunstructured.go @@ -88,12 +88,12 @@ func (m mapUnstructuredInterface) EqualsUsing(a Allocator, other Map) bool { } vv := a.allocValueUnstructured() defer a.Free(vv) - return other.Iterate(func(key string, value Value) bool { + return other.IterateUsing(a, func(key string, value Value) bool { lhsVal, ok := m[key] if !ok { return false } - return Equals(vv.reuse(lhsVal), value) + return EqualsUsing(a, vv.reuse(lhsVal), value) }) } @@ -168,12 +168,12 @@ func (m mapUnstructuredString) EqualsUsing(a Allocator, other Map) bool { } vv := a.allocValueUnstructured() defer a.Free(vv) - return other.Iterate(func(key string, value Value) bool { + return other.IterateUsing(a, func(key string, value Value) bool { lhsVal, ok := m[key] if !ok { return false } - return Equals(vv.reuse(lhsVal), value) + return EqualsUsing(a, vv.reuse(lhsVal), value) }) } From 6e15532cfebca2594bd09337ba72a691c4b29aa5 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 26 Jun 2023 15:42:59 -0700 Subject: [PATCH 041/105] Use EqualsUsing in Typed.Compare() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Results are mostly positive, with some huge gains in some cases and minor negative gains in others. ``` name old time/op new time/op delta DeducedSimple-8 57.5µs ± 0% 59.4µs ± 0% +3.19% (p=0.008 n=5+5) DeducedNested-8 123µs ± 0% 122µs ± 0% -0.50% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 167µs ± 0% 169µs ± 0% +1.11% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 77.6µs ± 0% 81.4µs ± 0% +4.80% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 1.15ms ± 0% 1.16ms ± 0% +0.54% (p=0.008 n=5+5) Operations/Pod/Create-8 69.6µs ± 0% 70.1µs ± 0% +0.68% (p=0.008 n=5+5) Operations/Pod/Apply-8 180µs ± 0% 177µs ± 0% -1.65% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 416µs ± 0% 414µs ± 0% -0.44% (p=0.008 n=5+5) Operations/Pod/Update-8 169µs ± 0% 173µs ± 0% +2.19% (p=0.008 n=5+5) Operations/Pod/UpdateVersion-8 241µs ± 0% 253µs ± 0% +4.80% (p=0.008 n=5+5) Operations/Node/Create-8 105µs ± 0% 107µs ± 0% +1.95% (p=0.008 n=5+5) Operations/Node/Apply-8 265µs ± 0% 277µs ± 0% +4.31% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 623µs ± 0% 658µs ± 0% +5.58% (p=0.008 n=5+5) Operations/Node/Update-8 268µs ± 0% 297µs ± 0% +11.01% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 390µs ± 0% 390µs ± 0% -0.12% (p=0.008 n=5+5) Operations/Endpoints/Create-8 7.01µs ± 0% 7.70µs ± 0% +9.78% (p=0.008 n=5+5) Operations/Endpoints/Apply-8 17.2µs ± 0% 16.4µs ± 0% -4.74% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 1.14ms ± 0% 0.98ms ± 0% -14.01% (p=0.008 n=5+5) Operations/Endpoints/Update-8 988µs ± 0% 874µs ± 0% -11.48% (p=0.008 n=5+5) Operations/Endpoints/UpdateVersion-8 2.26ms ± 0% 2.04ms ± 0% -9.82% (p=0.008 n=5+5) Operations/Node100%override/Create-8 110µs ± 0% 114µs ± 0% +3.70% (p=0.008 n=5+5) Operations/Node100%override/Apply-8 279µs ± 0% 276µs ± 0% -1.00% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 637µs ± 0% 606µs ± 0% -4.86% (p=0.008 n=5+5) Operations/Node100%override/Update-8 271µs ± 0% 287µs ± 0% +5.64% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 398µs ± 0% 383µs ± 0% -3.58% (p=0.008 n=5+5) Operations/Node10%override/Create-8 108µs ± 0% 107µs ± 0% -1.18% (p=0.008 n=5+5) Operations/Node10%override/Apply-8 268µs ± 0% 268µs ± 0% -0.09% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 624µs ± 0% 628µs ± 0% +0.74% (p=0.008 n=5+5) Operations/Node10%override/Update-8 267µs ± 0% 262µs ± 0% -2.06% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 402µs ± 0% 388µs ± 0% -3.61% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 7.08µs ± 0% 7.17µs ± 0% +1.33% (p=0.008 n=5+5) Operations/Endpoints100%override/Apply-8 15.6µs ± 0% 15.7µs ± 0% +0.27% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 990µs ± 0% 873µs ± 0% -11.82% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 980µs ± 0% 858µs ± 0% -12.43% (p=0.008 n=5+5) Operations/Endpoints100%override/UpdateVersion-8 1.93ms ± 0% 1.79ms ± 0% -7.31% (p=0.008 n=5+5) Operations/Endpoints10%override/Create-8 7.17µs ± 0% 7.33µs ± 0% +2.19% (p=0.008 n=5+5) Operations/Endpoints10%override/Apply-8 17.0µs ± 0% 16.0µs ± 0% -6.01% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 1.00ms ± 0% 0.88ms ± 0% -11.68% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 1.13ms ± 0% 0.86ms ± 0% -24.00% (p=0.008 n=5+5) Operations/Endpoints10%override/UpdateVersion-8 2.17ms ± 0% 1.81ms ± 0% -16.72% (p=0.008 n=5+5) Operations/PrometheusCRD/Create-8 582µs ± 0% 577µs ± 0% -0.78% (p=0.008 n=5+5) Operations/PrometheusCRD/Apply-8 1.54ms ± 0% 1.51ms ± 0% -2.22% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 3.75ms ± 0% 3.48ms ± 0% -7.19% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 1.95ms ± 0% 1.58ms ± 0% -18.88% (p=0.008 n=5+5) Operations/PrometheusCRD/UpdateVersion-8 2.75ms ± 0% 2.29ms ± 0% -16.68% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 2.93ms ± 0% 2.91ms ± 0% -0.65% (p=0.008 n=5+5) Operations/apiresourceimport/Apply-8 8.44ms ± 0% 8.08ms ± 0% -4.33% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 16.4ms ± 0% 14.9ms ± 0% -9.07% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 4.61ms ± 0% 5.32ms ± 0% +15.48% (p=0.008 n=5+5) Operations/apiresourceimport/UpdateVersion-8 6.32ms ± 0% 6.16ms ± 0% -2.57% (p=0.008 n=5+5) name old alloc/op new alloc/op delta DeducedSimple-8 28.4kB ± 0% 29.7kB ± 0% +4.32% (p=0.008 n=5+5) DeducedNested-8 56.7kB ± 0% 58.2kB ± 0% +2.65% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 75.6kB ± 0% 78.3kB ± 0% +3.45% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 40.2kB ± 0% 42.3kB ± 0% +5.33% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 714kB ± 0% 714kB ± 0% +0.11% (p=0.008 n=5+5) Operations/Pod/Create-8 21.0kB ± 0% 21.3kB ± 0% +1.47% (p=0.008 n=5+5) Operations/Pod/Apply-8 52.5kB ± 0% 52.8kB ± 0% +0.58% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 106kB ± 0% 107kB ± 0% +0.47% (p=0.008 n=5+5) Operations/Pod/Update-8 39.5kB ± 0% 40.0kB ± 0% +1.25% (p=0.008 n=5+5) Operations/Pod/UpdateVersion-8 51.9kB ± 0% 52.7kB ± 0% +1.42% (p=0.008 n=5+5) Operations/Node/Create-8 29.9kB ± 0% 30.2kB ± 0% +0.99% (p=0.008 n=5+5) Operations/Node/Apply-8 73.5kB ± 0% 73.8kB ± 0% +0.36% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 153kB ± 0% 149kB ± 0% -2.33% (p=0.008 n=5+5) Operations/Node/Update-8 60.9kB ± 0% 57.4kB ± 0% -5.75% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 83.3kB ± 0% 75.9kB ± 0% -8.86% (p=0.008 n=5+5) Operations/Endpoints/Create-8 3.54kB ± 0% 3.84kB ± 0% +8.59% (p=0.008 n=5+5) Operations/Endpoints/Apply-8 6.39kB ± 0% 6.70kB ± 0% +4.77% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 174kB ± 0% 110kB ± 0% -36.40% (p=0.008 n=5+5) Operations/Endpoints/Update-8 167kB ± 0% 104kB ± 0% -37.78% (p=0.008 n=5+5) Operations/Endpoints/UpdateVersion-8 329kB ± 0% 203kB ± 0% -38.46% (p=0.008 n=5+5) Operations/Node100%override/Create-8 29.9kB ± 0% 30.2kB ± 0% +1.00% (p=0.008 n=5+5) Operations/Node100%override/Apply-8 73.5kB ± 0% 73.8kB ± 0% +0.38% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 153kB ± 0% 149kB ± 0% -2.32% (p=0.008 n=5+5) Operations/Node100%override/Update-8 61.0kB ± 0% 57.4kB ± 0% -5.80% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 83.3kB ± 0% 75.9kB ± 0% -8.85% (p=0.008 n=5+5) Operations/Node10%override/Create-8 29.9kB ± 0% 30.2kB ± 0% +1.01% (p=0.008 n=5+5) Operations/Node10%override/Apply-8 73.5kB ± 0% 73.8kB ± 0% +0.38% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 153kB ± 0% 149kB ± 0% -2.37% (p=0.008 n=5+5) Operations/Node10%override/Update-8 60.9kB ± 0% 57.4kB ± 0% -5.77% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 83.3kB ± 0% 75.9kB ± 0% -8.84% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 3.54kB ± 0% 3.84kB ± 0% +8.62% (p=0.008 n=5+5) Operations/Endpoints100%override/Apply-8 6.39kB ± 0% 6.70kB ± 0% +4.77% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 174kB ± 0% 110kB ± 0% -36.40% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 167kB ± 0% 104kB ± 0% -37.77% (p=0.008 n=5+5) Operations/Endpoints100%override/UpdateVersion-8 329kB ± 0% 203kB ± 0% -38.48% (p=0.008 n=5+5) Operations/Endpoints10%override/Create-8 3.54kB ± 0% 3.84kB ± 0% +8.59% (p=0.008 n=5+5) Operations/Endpoints10%override/Apply-8 6.39kB ± 0% 6.70kB ± 0% +4.77% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 174kB ± 0% 110kB ± 0% -36.40% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 167kB ± 0% 104kB ± 0% -37.76% (p=0.008 n=5+5) Operations/Endpoints10%override/UpdateVersion-8 329kB ± 0% 203kB ± 0% -38.47% (p=0.008 n=5+5) Operations/PrometheusCRD/Create-8 165kB ± 0% 165kB ± 0% +0.15% (p=0.008 n=5+5) Operations/PrometheusCRD/Apply-8 471kB ± 0% 471kB ± 0% +0.06% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 959kB ± 0% 936kB ± 0% -2.46% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 342kB ± 0% 318kB ± 0% -7.08% (p=0.008 n=5+5) Operations/PrometheusCRD/UpdateVersion-8 473kB ± 0% 425kB ± 0% -10.16% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 679kB ± 0% 679kB ± 0% +0.03% (p=0.008 n=5+5) Operations/apiresourceimport/Apply-8 1.94MB ± 0% 1.94MB ± 0% -0.04% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 3.60MB ± 0% 3.57MB ± 0% -0.88% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 1.05MB ± 0% 1.02MB ± 0% -2.86% (p=0.008 n=5+5) Operations/apiresourceimport/UpdateVersion-8 1.41MB ± 0% 1.35MB ± 0% -4.19% (p=0.008 n=5+5) name old allocs/op new allocs/op delta DeducedSimple-8 671 ± 0% 703 ± 0% +4.77% (p=0.008 n=5+5) DeducedNested-8 1.29k ± 0% 1.34k ± 0% +3.32% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 1.75k ± 0% 1.83k ± 0% +4.51% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 887 ± 0% 943 ± 0% +6.31% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 7.53k ± 0% 7.58k ± 0% +0.64% (p=0.008 n=5+5) Operations/Pod/Create-8 473 ± 0% 481 ± 0% +1.69% (p=0.008 n=5+5) Operations/Pod/Apply-8 1.25k ± 0% 1.26k ± 0% +0.64% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 2.61k ± 0% 2.62k ± 0% +0.23% (p=0.008 n=5+5) Operations/Pod/Update-8 971 ± 0% 976 ± 0% +0.51% (p=0.008 n=5+5) Operations/Pod/UpdateVersion-8 1.40k ± 0% 1.40k ± 0% +0.36% (p=0.008 n=5+5) Operations/Node/Create-8 557 ± 0% 565 ± 0% +1.44% (p=0.008 n=5+5) Operations/Node/Apply-8 1.56k ± 0% 1.57k ± 0% +0.45% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 3.51k ± 0% 3.37k ± 0% -4.07% (p=0.008 n=5+5) Operations/Node/Update-8 1.41k ± 0% 1.26k ± 0% -10.09% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 2.16k ± 0% 1.87k ± 0% -13.64% (p=0.008 n=5+5) Operations/Endpoints/Create-8 76.0 ± 0% 84.0 ± 0% +10.53% (p=0.008 n=5+5) Operations/Endpoints/Apply-8 148 ± 0% 156 ± 0% +5.41% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 6.33k ± 0% 2.35k ± 0% -62.91% (p=0.008 n=5+5) Operations/Endpoints/Update-8 6.17k ± 0% 2.20k ± 0% -64.44% (p=0.008 n=5+5) Operations/Endpoints/UpdateVersion-8 12.2k ± 0% 4.3k ± 0% -65.09% (p=0.008 n=5+5) Operations/Node100%override/Create-8 557 ± 0% 565 ± 0% +1.44% (p=0.008 n=5+5) Operations/Node100%override/Apply-8 1.56k ± 0% 1.57k ± 0% +0.51% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 3.51k ± 0% 3.37k ± 0% -4.07% (p=0.008 n=5+5) Operations/Node100%override/Update-8 1.41k ± 0% 1.26k ± 0% -10.16% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 2.16k ± 0% 1.87k ± 0% -13.59% (p=0.008 n=5+5) Operations/Node10%override/Create-8 557 ± 0% 565 ± 0% +1.44% (p=0.008 n=5+5) Operations/Node10%override/Apply-8 1.56k ± 0% 1.57k ± 0% +0.51% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 3.51k ± 0% 3.37k ± 0% -4.10% (p=0.008 n=5+5) Operations/Node10%override/Update-8 1.41k ± 0% 1.26k ± 0% -10.16% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 2.16k ± 0% 1.87k ± 0% -13.64% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 76.0 ± 0% 84.0 ± 0% +10.53% (p=0.008 n=5+5) Operations/Endpoints100%override/Apply-8 148 ± 0% 156 ± 0% +5.41% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 6.33k ± 0% 2.35k ± 0% -62.90% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 6.17k ± 0% 2.20k ± 0% -64.44% (p=0.008 n=5+5) Operations/Endpoints100%override/UpdateVersion-8 12.2k ± 0% 4.3k ± 0% -65.09% (p=0.008 n=5+5) Operations/Endpoints10%override/Create-8 76.0 ± 0% 84.0 ± 0% +10.53% (p=0.008 n=5+5) Operations/Endpoints10%override/Apply-8 148 ± 0% 156 ± 0% +5.41% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 6.33k ± 0% 2.35k ± 0% -62.90% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 6.17k ± 0% 2.20k ± 0% -64.44% (p=0.008 n=5+5) Operations/Endpoints10%override/UpdateVersion-8 12.2k ± 0% 4.3k ± 0% -65.09% (p=0.008 n=5+5) Operations/PrometheusCRD/Create-8 3.67k ± 0% 3.68k ± 0% +0.19% (p=0.008 n=5+5) Operations/PrometheusCRD/Apply-8 10.2k ± 0% 10.2k ± 0% +0.08% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 21.2k ± 0% 20.0k ± 0% -5.89% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 8.23k ± 0% 6.98k ± 0% -15.26% (p=0.008 n=5+5) Operations/PrometheusCRD/UpdateVersion-8 12.7k ± 0% 10.2k ± 0% -19.83% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 15.4k ± 0% 15.4k ± 0% +0.05% (p=0.008 n=5+5) Operations/apiresourceimport/Apply-8 43.0k ± 0% 43.0k ± 0% -0.01% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 82.8k ± 0% 81.8k ± 0% -1.25% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 27.4k ± 0% 26.3k ± 0% -3.72% (p=0.008 n=5+5) Operations/apiresourceimport/UpdateVersion-8 39.3k ± 0% 37.3k ± 0% -5.17% (p=0.008 n=5+5) ``` --- typed/typed.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/typed/typed.go b/typed/typed.go index 5071e865..6411bd51 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -129,12 +129,13 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) { Modified: fieldpath.NewSet(), Added: fieldpath.NewSet(), } + a := value.NewFreelistAllocator() _, err = merge(&tv, rhs, func(w *mergingWalker) { if w.lhs == nil { c.Added.Insert(w.path) } else if w.rhs == nil { c.Removed.Insert(w.path) - } else if !value.Equals(w.rhs, w.lhs) { + } else if !value.EqualsUsing(a, w.rhs, w.lhs) { // TODO: Equality is not sufficient for this. // Need to implement equality check on the value type. c.Modified.Insert(w.path) From 6310a8aeb8c2df9772afad9c5c75b4ff8f0bc3bf Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 23 Jun 2023 10:43:02 -0700 Subject: [PATCH 042/105] Do not use Compare for Equality checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we use the Compare function to compare objects, but this doesn't look at the actual differences between objects, it collects the fields from each object using smd logic and then compares that. This has a couple of drawbacks: 1. Order is not preserved, which means anything that depends on order is wrong! 2. When we decide to allow invalid duplicates, we won't be able to compare such objects. Use value.Equals to compare objects so that we have proper comparison, and cmp.Diff in tests to highlight the differences. As a consequence of 1, this has identified an interesting bug: we used the comparison to see if two objects (before and after) were equal to return nil as an output of `Apply`, but that logic doesn't check if the order has changed, which caused some tests to fail (since only the order had changed, and that change was ignored, the object wasn't updated). Using Equals instead of re-using the compare result in applying has an extra cost that can be noticed on ApplyTwice test-suite, as shown below: ``` name old time/op new time/op delta DeducedSimple-8 60.7µs ± 0% 62.1µs ± 0% +2.21% (p=0.008 n=5+5) DeducedNested-8 131µs ± 0% 131µs ± 0% -0.19% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 171µs ± 0% 174µs ± 0% +1.89% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 83.2µs ± 0% 83.2µs ± 0% +0.11% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 1.15ms ± 0% 1.17ms ± 0% +1.59% (p=0.008 n=5+5) Operations/Pod/Create-8 72.5µs ± 0% 70.9µs ± 0% -2.29% (p=0.008 n=5+5) Operations/Pod/Apply-8 184µs ± 0% 182µs ± 0% -0.81% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 416µs ± 0% 457µs ± 0% +10.03% (p=0.008 n=5+5) Operations/Pod/Update-8 181µs ± 0% 178µs ± 0% -1.62% (p=0.008 n=5+5) Operations/Pod/UpdateVersion-8 259µs ± 0% 256µs ± 0% -0.86% (p=0.008 n=5+5) Operations/Node/Create-8 110µs ± 0% 111µs ± 0% +1.18% (p=0.008 n=5+5) Operations/Node/Apply-8 283µs ± 0% 275µs ± 0% -2.97% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 661µs ± 0% 685µs ± 0% +3.64% (p=0.008 n=5+5) Operations/Node/Update-8 344µs ± 0% 275µs ± 0% -20.20% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 383µs ± 0% 401µs ± 0% +4.60% (p=0.008 n=5+5) Operations/Endpoints/Create-8 7.46µs ± 0% 9.98µs ± 0% +33.75% (p=0.008 n=5+5) Operations/Endpoints/Apply-8 16.6µs ± 0% 19.0µs ± 0% +14.22% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 930µs ± 0% 4300µs ± 0% +362.33% (p=0.008 n=5+5) Operations/Endpoints/Update-8 882µs ± 0% 1241µs ± 0% +40.70% (p=0.008 n=5+5) Operations/Endpoints/UpdateVersion-8 1.90ms ± 0% 2.24ms ± 0% +17.77% (p=0.008 n=5+5) Operations/Node100%override/Create-8 108µs ± 0% 130µs ± 0% +20.77% (p=0.008 n=5+5) Operations/Node100%override/Apply-8 273µs ± 0% 307µs ± 0% +12.56% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 630µs ± 0% 688µs ± 0% +9.26% (p=0.008 n=5+5) Operations/Node100%override/Update-8 281µs ± 0% 290µs ± 0% +3.23% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 402µs ± 0% 416µs ± 0% +3.40% (p=0.008 n=5+5) Operations/Node10%override/Create-8 110µs ± 0% 119µs ± 0% +7.80% (p=0.008 n=5+5) Operations/Node10%override/Apply-8 272µs ± 0% 307µs ± 0% +12.74% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 640µs ± 0% 711µs ± 0% +11.19% (p=0.008 n=5+5) Operations/Node10%override/Update-8 272µs ± 0% 281µs ± 0% +3.12% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 407µs ± 0% 472µs ± 0% +16.00% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 7.66µs ± 0% 8.93µs ± 0% +16.56% (p=0.008 n=5+5) Operations/Endpoints100%override/Apply-8 16.4µs ± 0% 22.3µs ± 0% +35.78% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 889µs ± 0% 2445µs ± 0% +175.06% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 872µs ± 0% 1009µs ± 0% +15.68% (p=0.008 n=5+5) Operations/Endpoints100%override/UpdateVersion-8 1.97ms ± 0% 2.28ms ± 0% +15.72% (p=0.008 n=5+5) Operations/Endpoints10%override/Create-8 7.62µs ± 0% 11.83µs ± 0% +55.25% (p=0.008 n=5+5) Operations/Endpoints10%override/Apply-8 16.5µs ± 0% 26.4µs ± 0% +59.77% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 901µs ± 0% 4497µs ± 0% +399.05% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 879µs ± 0% 1034µs ± 0% +17.57% (p=0.008 n=5+5) Operations/Endpoints10%override/UpdateVersion-8 1.96ms ± 0% 2.48ms ± 0% +26.24% (p=0.008 n=5+5) Operations/PrometheusCRD/Create-8 646µs ± 0% 625µs ± 0% -3.21% (p=0.008 n=5+5) Operations/PrometheusCRD/Apply-8 1.54ms ± 0% 1.57ms ± 0% +2.12% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 3.61ms ± 0% 4.23ms ± 0% +16.93% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 1.56ms ± 0% 1.68ms ± 0% +7.56% (p=0.008 n=5+5) Operations/PrometheusCRD/UpdateVersion-8 2.29ms ± 0% 2.48ms ± 0% +8.42% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 3.61ms ± 0% 3.05ms ± 0% -15.32% (p=0.008 n=5+5) Operations/apiresourceimport/Apply-8 8.12ms ± 0% 8.41ms ± 0% +3.64% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 16.3ms ± 0% 17.1ms ± 0% +4.42% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 4.88ms ± 0% 5.00ms ± 0% +2.57% (p=0.008 n=5+5) Operations/apiresourceimport/UpdateVersion-8 6.50ms ± 0% 6.87ms ± 0% +5.80% (p=0.008 n=5+5) name old alloc/op new alloc/op delta DeducedSimple-8 29.7kB ± 0% 30.4kB ± 0% +2.35% (p=0.008 n=5+5) DeducedNested-8 58.3kB ± 0% 59.1kB ± 0% +1.39% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 78.2kB ± 0% 79.1kB ± 0% +1.16% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 42.3kB ± 0% 43.0kB ± 0% +1.69% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 714kB ± 0% 716kB ± 0% +0.29% (p=0.008 n=5+5) Operations/Pod/Create-8 21.3kB ± 0% 21.3kB ± 0% +0.02% (p=0.008 n=5+5) Operations/Pod/Apply-8 52.8kB ± 0% 53.1kB ± 0% +0.56% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 107kB ± 0% 110kB ± 0% +3.60% (p=0.008 n=5+5) Operations/Pod/Update-8 40.0kB ± 0% 40.0kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Pod/UpdateVersion-8 52.6kB ± 0% 52.6kB ± 0% +0.03% (p=0.008 n=5+5) Operations/Node/Create-8 30.2kB ± 0% 30.2kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Node/Apply-8 73.8kB ± 0% 74.1kB ± 0% +0.38% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 149kB ± 0% 155kB ± 0% +4.24% (p=0.008 n=5+5) Operations/Node/Update-8 57.4kB ± 0% 57.4kB ± 0% -0.03% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 75.9kB ± 0% 75.9kB ± 0% +0.01% (p=0.008 n=5+5) Operations/Endpoints/Create-8 3.84kB ± 0% 3.84kB ± 0% ~ (all equal) Operations/Endpoints/Apply-8 6.70kB ± 0% 6.99kB ± 0% +4.28% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 110kB ± 0% 208kB ± 0% +88.69% (p=0.008 n=5+5) Operations/Endpoints/Update-8 104kB ± 0% 104kB ± 0% +0.00% (p=0.008 n=5+5) Operations/Endpoints/UpdateVersion-8 203kB ± 0% 202kB ± 0% -0.05% (p=0.008 n=5+5) Operations/Node100%override/Create-8 30.2kB ± 0% 30.2kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Node100%override/Apply-8 73.8kB ± 0% 74.1kB ± 0% +0.43% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 149kB ± 0% 155kB ± 0% +4.19% (p=0.008 n=5+5) Operations/Node100%override/Update-8 57.4kB ± 0% 57.4kB ± 0% -0.05% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 75.9kB ± 0% 75.9kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Node10%override/Create-8 30.2kB ± 0% 30.2kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Node10%override/Apply-8 73.8kB ± 0% 74.1kB ± 0% +0.36% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 149kB ± 0% 155kB ± 0% +4.17% (p=0.008 n=5+5) Operations/Node10%override/Update-8 57.4kB ± 0% 57.4kB ± 0% -0.02% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 75.9kB ± 0% 75.9kB ± 0% +0.01% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 3.84kB ± 0% 3.84kB ± 0% ~ (all equal) Operations/Endpoints100%override/Apply-8 6.70kB ± 0% 6.99kB ± 0% +4.30% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 110kB ± 0% 208kB ± 0% +88.60% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 104kB ± 0% 104kB ± 0% -0.01% (p=0.008 n=5+5) Operations/Endpoints100%override/UpdateVersion-8 203kB ± 0% 203kB ± 0% -0.03% (p=0.008 n=5+5) Operations/Endpoints10%override/Create-8 3.84kB ± 0% 3.84kB ± 0% +0.03% (p=0.008 n=5+5) Operations/Endpoints10%override/Apply-8 6.70kB ± 0% 6.99kB ± 0% +4.32% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 110kB ± 0% 208kB ± 0% +88.58% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 104kB ± 0% 104kB ± 0% +0.01% (p=0.008 n=5+5) Operations/Endpoints10%override/UpdateVersion-8 203kB ± 0% 203kB ± 0% -0.02% (p=0.008 n=5+5) Operations/PrometheusCRD/Create-8 165kB ± 0% 165kB ± 0% ~ (all equal) Operations/PrometheusCRD/Apply-8 471kB ± 0% 471kB ± 0% +0.13% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 935kB ± 0% 983kB ± 0% +5.11% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 318kB ± 0% 318kB ± 0% -0.01% (p=0.008 n=5+5) Operations/PrometheusCRD/UpdateVersion-8 425kB ± 0% 424kB ± 0% -0.11% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 679kB ± 0% 679kB ± 0% -0.00% (p=0.008 n=5+5) Operations/apiresourceimport/Apply-8 1.94MB ± 0% 1.94MB ± 0% +0.11% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 3.56MB ± 0% 3.65MB ± 0% +2.42% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 1.02MB ± 0% 1.02MB ± 0% +0.00% (p=0.008 n=5+5) Operations/apiresourceimport/UpdateVersion-8 1.35MB ± 0% 1.35MB ± 0% +0.01% (p=0.008 n=5+5) name old allocs/op new allocs/op delta DeducedSimple-8 703 ± 0% 724 ± 0% +2.99% (p=0.008 n=5+5) DeducedNested-8 1.34k ± 0% 1.36k ± 0% +1.72% (p=0.008 n=5+5) DeducedNestedAcrossVersion-8 1.83k ± 0% 1.85k ± 0% +1.37% (p=0.008 n=5+5) LeafConflictAcrossVersion-8 943 ± 0% 964 ± 0% +2.23% (p=0.008 n=5+5) MultipleApplierRecursiveRealConversion-8 7.57k ± 0% 7.62k ± 0% +0.67% (p=0.008 n=5+5) Operations/Pod/Create-8 481 ± 0% 481 ± 0% ~ (all equal) Operations/Pod/Apply-8 1.26k ± 0% 1.27k ± 0% +0.63% (p=0.008 n=5+5) Operations/Pod/ApplyTwice-8 2.62k ± 0% 2.72k ± 0% +3.97% (p=0.008 n=5+5) Operations/Pod/Update-8 976 ± 0% 976 ± 0% ~ (all equal) Operations/Pod/UpdateVersion-8 1.40k ± 0% 1.40k ± 0% ~ (all equal) Operations/Node/Create-8 565 ± 0% 565 ± 0% ~ (all equal) Operations/Node/Apply-8 1.57k ± 0% 1.58k ± 0% +0.51% (p=0.008 n=5+5) Operations/Node/ApplyTwice-8 3.37k ± 0% 3.56k ± 0% +5.58% (p=0.008 n=5+5) Operations/Node/Update-8 1.26k ± 0% 1.26k ± 0% -0.08% (p=0.008 n=5+5) Operations/Node/UpdateVersion-8 1.87k ± 0% 1.87k ± 0% ~ (all equal) Operations/Endpoints/Create-8 84.0 ± 0% 84.0 ± 0% ~ (all equal) Operations/Endpoints/Apply-8 156 ± 0% 164 ± 0% +5.13% (p=0.008 n=5+5) Operations/Endpoints/ApplyTwice-8 2.35k ± 0% 4.40k ± 0% +87.52% (p=0.008 n=5+5) Operations/Endpoints/Update-8 2.20k ± 0% 2.20k ± 0% ~ (all equal) Operations/Endpoints/UpdateVersion-8 4.27k ± 0% 4.27k ± 0% -0.02% (p=0.008 n=5+5) Operations/Node100%override/Create-8 565 ± 0% 565 ± 0% ~ (all equal) Operations/Node100%override/Apply-8 1.57k ± 0% 1.58k ± 0% +0.57% (p=0.008 n=5+5) Operations/Node100%override/ApplyTwice-8 3.37k ± 0% 3.56k ± 0% +5.55% (p=0.008 n=5+5) Operations/Node100%override/Update-8 1.26k ± 0% 1.26k ± 0% -0.08% (p=0.008 n=5+5) Operations/Node100%override/UpdateVersion-8 1.87k ± 0% 1.87k ± 0% ~ (all equal) Operations/Node10%override/Create-8 565 ± 0% 565 ± 0% ~ (all equal) Operations/Node10%override/Apply-8 1.57k ± 0% 1.58k ± 0% +0.51% (p=0.008 n=5+5) Operations/Node10%override/ApplyTwice-8 3.37k ± 0% 3.56k ± 0% +5.52% (p=0.008 n=5+5) Operations/Node10%override/Update-8 1.26k ± 0% 1.26k ± 0% -0.08% (p=0.008 n=5+5) Operations/Node10%override/UpdateVersion-8 1.87k ± 0% 1.87k ± 0% +0.05% (p=0.008 n=5+5) Operations/Endpoints100%override/Create-8 84.0 ± 0% 84.0 ± 0% ~ (all equal) Operations/Endpoints100%override/Apply-8 156 ± 0% 164 ± 0% +5.13% (p=0.008 n=5+5) Operations/Endpoints100%override/ApplyTwice-8 2.35k ± 0% 4.40k ± 0% +87.44% (p=0.008 n=5+5) Operations/Endpoints100%override/Update-8 2.20k ± 0% 2.20k ± 0% ~ (all equal) Operations/Endpoints100%override/UpdateVersion-8 4.27k ± 0% 4.27k ± 0% ~ (all equal) Operations/Endpoints10%override/Create-8 84.0 ± 0% 84.0 ± 0% ~ (all equal) Operations/Endpoints10%override/Apply-8 156 ± 0% 164 ± 0% +5.13% (p=0.008 n=5+5) Operations/Endpoints10%override/ApplyTwice-8 2.35k ± 0% 4.40k ± 0% +87.47% (p=0.008 n=5+5) Operations/Endpoints10%override/Update-8 2.20k ± 0% 2.20k ± 0% ~ (all equal) Operations/Endpoints10%override/UpdateVersion-8 4.27k ± 0% 4.27k ± 0% ~ (all equal) Operations/PrometheusCRD/Create-8 3.68k ± 0% 3.68k ± 0% ~ (all equal) Operations/PrometheusCRD/Apply-8 10.2k ± 0% 10.2k ± 0% +0.10% (p=0.008 n=5+5) Operations/PrometheusCRD/ApplyTwice-8 20.0k ± 0% 21.1k ± 0% +5.56% (p=0.008 n=5+5) Operations/PrometheusCRD/Update-8 6.98k ± 0% 6.98k ± 0% ~ (all equal) Operations/PrometheusCRD/UpdateVersion-8 10.2k ± 0% 10.2k ± 0% -0.05% (p=0.008 n=5+5) Operations/apiresourceimport/Create-8 15.4k ± 0% 15.4k ± 0% ~ (all equal) Operations/apiresourceimport/Apply-8 43.0k ± 0% 43.0k ± 0% +0.06% (p=0.008 n=5+5) Operations/apiresourceimport/ApplyTwice-8 81.7k ± 0% 83.8k ± 0% +2.56% (p=0.008 n=5+5) Operations/apiresourceimport/Update-8 26.3k ± 0% 26.3k ± 0% ~ (all equal) Operations/apiresourceimport/UpdateVersion-8 37.3k ± 0% 37.3k ± 0% ~ (all equal) ``` --- go.mod | 1 + go.sum | 2 ++ internal/fixture/state.go | 44 +++++++++++++++++++++++++++------ merge/obsolete_versions_test.go | 2 +- merge/set_test.go | 8 +++--- merge/update.go | 8 +++--- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 0f5fe4b5..2133f8c4 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module sigs.k8s.io/structured-merge-diff/v4 require gopkg.in/yaml.v2 v2.2.8 require ( + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.0.0 github.com/json-iterator/go v1.1.12 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index bf04c8ea..a37122bb 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 80d87ced..814d809e 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -20,10 +20,11 @@ import ( "bytes" "fmt" + "github.com/google/go-cmp/cmp" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/merge" "sigs.k8s.io/structured-merge-diff/v4/typed" - "sigs.k8s.io/structured-merge-diff/v4/value" ) // For the sake of tests, a parser is something that can retrieve a @@ -168,20 +169,47 @@ func (s *State) Apply(obj typed.YAMLObject, version fieldpath.APIVersion, manage // CompareLive takes a YAML string and returns the comparison with the // current live object or an error. -func (s *State) CompareLive(obj typed.YAMLObject, version fieldpath.APIVersion) (*typed.Comparison, error) { +func (s *State) CompareLive(obj typed.YAMLObject, version fieldpath.APIVersion) (string, error) { obj = FixTabsOrDie(obj) if err := s.checkInit(version); err != nil { - return nil, err + return "", err } tv, err := s.Parser.Type(string(version)).FromYAML(obj) if err != nil { - return nil, err + return "", err } live, err := s.Updater.Converter.Convert(s.Live, version) if err != nil { - return nil, err + return "", err + } + tvu := convertMapAnyToMapString(tv.AsValue().Unstructured()) + liveu := convertMapAnyToMapString(live.AsValue().Unstructured()) + return cmp.Diff(tvu, liveu), nil +} + +func convertMapAnyToMapString(obj interface{}) interface{} { + switch m := obj.(type) { + case map[string]interface{}: + out := map[string]interface{}{} + for key, value := range m { + out[key] = convertMapAnyToMapString(value) + } + return out + case map[interface{}]interface{}: + out := map[string]interface{}{} + for key, value := range m { + out[key.(string)] = convertMapAnyToMapString(value) + } + return out + case []interface{}: + out := []interface{}{} + for _, value := range m { + out = append(out, convertMapAnyToMapString(value)) + } + return out + default: + return obj } - return live.Compare(tv) } // dummyConverter doesn't convert, it just returns the same exact object, as long as a version is provided. @@ -581,8 +609,8 @@ func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) e if err != nil { return fmt.Errorf("failed to compare live with config: %v", err) } - if !comparison.IsSame() { - return fmt.Errorf("expected live and config to be the same:\n%v\nConfig: %v\n", comparison, value.ToString(state.Live.AsValue())) + if comparison != "" { + return fmt.Errorf("expected live and config to be the same:\n%v\n", comparison) } } diff --git a/merge/obsolete_versions_test.go b/merge/obsolete_versions_test.go index fdb119eb..3073a198 100644 --- a/merge/obsolete_versions_test.go +++ b/merge/obsolete_versions_test.go @@ -127,7 +127,7 @@ func TestApplyObsoleteVersion(t *testing.T) { if err != nil { t.Fatalf("Failed to compare live object: %v", err) } - if !comparison.IsSame() { + if comparison != "" { t.Fatalf("Unexpected object:\n%v", comparison) } } diff --git a/merge/set_test.go b/merge/set_test.go index 5882c6ef..e1209926 100644 --- a/merge/set_test.go +++ b/merge/set_test.go @@ -124,11 +124,11 @@ func TestUpdateSet(t *testing.T) { Object: ` list: - a - - aprime - b + - aprime - c - - cprime - d + - cprime `, APIVersion: "v1", Managed: fieldpath.ManagedFields{ @@ -189,11 +189,11 @@ func TestUpdateSet(t *testing.T) { Object: ` list: - a - - aprime - b + - aprime - c - - cprime - d + - cprime `, APIVersion: "v1", Managed: fieldpath.ManagedFields{ diff --git a/merge/update.go b/merge/update.go index 95ab2e78..a6b371d1 100644 --- a/merge/update.go +++ b/merge/update.go @@ -18,6 +18,7 @@ import ( "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/typed" + "sigs.k8s.io/structured-merge-diff/v4/value" ) // Converter is an interface to the conversion logic. The converter @@ -157,8 +158,7 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp // Apply should be called when Apply is run, given the current object as // well as the configuration that is applied. This will merge the object -// and return it. If the object hasn't changed, nil is returned (the -// managers can still have changed though). +// and return it. func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, manager string, force bool) (*typed.TypedValue, fieldpath.ManagedFields, error) { var err error managers, err = s.reconcileManagedFieldsWithSchemaChanges(liveObject, managers) @@ -200,11 +200,11 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel if err != nil { return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to prune fields: %v", err) } - managers, compare, err := s.update(liveObject, newObject, version, managers, manager, force) + managers, _, err = s.update(liveObject, newObject, version, managers, manager, force) if err != nil { return nil, fieldpath.ManagedFields{}, err } - if compare.IsSame() { + if value.EqualsUsing(value.NewFreelistAllocator(), liveObject.AsValue(), newObject.AsValue()) { newObject = nil } return newObject, managers, nil From d1b78eb4dd4b5a4f56ee546a2e3157f86bb3044d Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 29 Jun 2023 14:06:44 -0700 Subject: [PATCH 043/105] merge: Add flag to skip comparison after apply Currently we have an expensive (especially since the parent commit) comparison after applying an object. This is mostly unuseful considering that we have a piece of code in kubernetes that removes entries that are no-ops. Provide a new flag so that the comparison can be disabled, especially since the comparison is done somewhere else, and the comparison is not good enough anyway. ``` BenchmarkOperations/Pod/ApplyTwice-8 267 437527 ns/op 110382 B/op 2722 allocs/op BenchmarkOperations/Pod/ApplyTwiceNoCompare-8 289 412029 ns/op 106516 B/op 2619 allocs/op BenchmarkOperations/Node/ApplyTwice-8 176 828565 ns/op 155376 B/op 3557 allocs/op BenchmarkOperations/Node/ApplyTwiceNoCompare-8 100 1179327 ns/op 149046 B/op 3370 allocs/op BenchmarkOperations/Endpoints/ApplyTwice-8 50 2268639 ns/op 208438 B/op 4402 allocs/op BenchmarkOperations/Endpoints/ApplyTwiceNoCompare-8 126 921992 ns/op 110419 B/op 2347 allocs/op BenchmarkOperations/Node100%override/ApplyTwice-8 174 669942 ns/op 155305 B/op 3556 allocs/op BenchmarkOperations/Node100%override/ApplyTwiceNoCompare-8 190 634832 ns/op 149025 B/op 3369 allocs/op BenchmarkOperations/Node10%override/ApplyTwice-8 157 691645 ns/op 155316 B/op 3557 allocs/op BenchmarkOperations/Node10%override/ApplyTwiceNoCompare-8 186 635949 ns/op 149036 B/op 3370 allocs/op BenchmarkOperations/Endpoints100%override/ApplyTwice-8 63 1811443 ns/op 208261 B/op 4400 allocs/op BenchmarkOperations/Endpoints100%override/ApplyTwiceNoCompare-8 127 932614 ns/op 110431 B/op 2347 allocs/op BenchmarkOperations/Endpoints10%override/ApplyTwice-8 58 1903112 ns/op 208359 B/op 4401 allocs/op BenchmarkOperations/Endpoints10%override/ApplyTwiceNoCompare-8 124 957186 ns/op 110426 B/op 2347 allocs/op BenchmarkOperations/PrometheusCRD/ApplyTwice-8 30 3843649 ns/op 983421 B/op 21105 allocs/op BenchmarkOperations/PrometheusCRD/ApplyTwiceNoCompare-8 34 3500118 ns/op 935164 B/op 19987 allocs/op BenchmarkOperations/apiresourceimport/ApplyTwice-8 7 15743401 ns/op 3650892 B/op 83842 allocs/op BenchmarkOperations/apiresourceimport/ApplyTwiceNoCompare-8 7 15123009 ns/op 3566088 B/op 81776 allocs/op ``` --- internal/fixture/state.go | 25 +++++++++++++++++-------- merge/real_test.go | 24 +++++++++++++++++++++--- merge/update.go | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 814d809e..09463d1a 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -537,6 +537,9 @@ type TestCase struct { Managed fieldpath.ManagedFields // Set to true if the test case needs the union behavior enabled. RequiresUnions bool + // ReportInputOnNoop if we don't want to compare the output and + // always return it. + ReturnInputOnNoop bool // IgnoredFields containing the set to ignore for every version IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set } @@ -569,13 +572,16 @@ func (tc TestCase) PreprocessOperations(parser Parser) error { // TestWithConverter once and reset the benchmark, to make sure the test case // actually passes.. func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) error { + updaterBuilder := merge.UpdaterBuilder{ + Converter: converter, + IgnoredFields: tc.IgnoredFields, + ReturnInputOnNoop: tc.ReturnInputOnNoop, + EnableUnions: tc.RequiresUnions, + } state := State{ - Updater: &merge.Updater{Converter: converter, IgnoredFields: tc.IgnoredFields}, + Updater: updaterBuilder.BuildUpdater(), Parser: parser, } - if tc.RequiresUnions { - state.Updater.EnableUnionFeature() - } // We currently don't have any test that converts, we can take // care of that later. for i, ops := range tc.Ops { @@ -589,13 +595,16 @@ func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) // TestWithConverter runs the test-case using the given parser and converter. func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) error { + updaterBuilder := merge.UpdaterBuilder{ + Converter: converter, + IgnoredFields: tc.IgnoredFields, + ReturnInputOnNoop: tc.ReturnInputOnNoop, + EnableUnions: tc.RequiresUnions, + } state := State{ - Updater: &merge.Updater{Converter: converter, IgnoredFields: tc.IgnoredFields}, + Updater: updaterBuilder.BuildUpdater(), Parser: parser, } - if tc.RequiresUnions { - state.Updater.EnableUnionFeature() - } for i, ops := range tc.Ops { err := ops.run(&state) if err != nil { diff --git a/merge/real_test.go b/merge/real_test.go index 2620c43d..9a1cc9e9 100644 --- a/merge/real_test.go +++ b/merge/real_test.go @@ -108,8 +108,9 @@ func BenchmarkOperations(b *testing.B) { b.Run(bench.name, func(b *testing.B) { obj := typed.YAMLObject(read(testdata(bench.filename))) tests := []struct { - name string - ops []Operation + name string + returnInputonNoop bool + ops []Operation }{ { name: "Create", @@ -146,6 +147,22 @@ func BenchmarkOperations(b *testing.B) { }, }, }, + { + name: "ApplyTwiceNoCompare", + returnInputonNoop: true, + ops: []Operation{ + Apply{ + Manager: "controller", + APIVersion: "v1", + Object: obj, + }, + Apply{ + Manager: "other-controller", + APIVersion: "v1", + Object: obj, + }, + }, + }, { name: "Update", ops: []Operation{ @@ -180,7 +197,8 @@ func BenchmarkOperations(b *testing.B) { for _, test := range tests { b.Run(test.name, func(b *testing.B) { tc := TestCase{ - Ops: test.ops, + Ops: test.ops, + ReturnInputOnNoop: test.returnInputonNoop, } p := SameVersionParser{T: bench.parseType} tc.PreprocessOperations(p) diff --git a/merge/update.go b/merge/update.go index a6b371d1..e1540841 100644 --- a/merge/update.go +++ b/merge/update.go @@ -28,17 +28,50 @@ type Converter interface { IsMissingVersionError(error) bool } +// UpdateBuilder allows you to create a new Updater by exposing all of +// the options and setting them once. +type UpdaterBuilder struct { + Converter Converter + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + + EnableUnions bool + + // Stop comparing the new object with old object after applying. + // This was initially used to avoid spurious etcd update, but + // since that's vastly inefficient, we've come-up with a better + // way of doing that. Create this flag to stop it. + // Comparing has become more expensive too now that we're not using + // `Compare` but `value.Equals` so this gives an option to avoid it. + ReturnInputOnNoop bool +} + +func (u *UpdaterBuilder) BuildUpdater() *Updater { + return &Updater{ + Converter: u.Converter, + IgnoredFields: u.IgnoredFields, + enableUnions: u.EnableUnions, + returnInputOnNoop: u.ReturnInputOnNoop, + } +} + // Updater is the object used to compute updated FieldSets and also // merge the object on Apply. type Updater struct { - Converter Converter + // Deprecated: This will eventually become private. + Converter Converter + + // Deprecated: This will eventually become private. IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set enableUnions bool + + returnInputOnNoop bool } // EnableUnionFeature turns on union handling. It is disabled by default until the // feature is complete. +// +// Deprecated: Use the builder instead. func (s *Updater) EnableUnionFeature() { s.enableUnions = true } @@ -204,7 +237,7 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel if err != nil { return nil, fieldpath.ManagedFields{}, err } - if value.EqualsUsing(value.NewFreelistAllocator(), liveObject.AsValue(), newObject.AsValue()) { + if !s.returnInputOnNoop && value.EqualsUsing(value.NewFreelistAllocator(), liveObject.AsValue(), newObject.AsValue()) { newObject = nil } return newObject, managers, nil From 24aa83d37c171bd3eae02df56a85d76426cdc3bd Mon Sep 17 00:00:00 2001 From: Keerthan Reddy Mala Date: Thu, 6 Jul 2023 17:13:36 -0700 Subject: [PATCH 044/105] deep copy slice object during append --- merge/conflict.go | 2 +- merge/conflict_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/merge/conflict.go b/merge/conflict.go index 75a492d8..f1aa2586 100644 --- a/merge/conflict.go +++ b/merge/conflict.go @@ -112,7 +112,7 @@ func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts { set.Set().Iterate(func(p fieldpath.Path) { conflicts = append(conflicts, Conflict{ Manager: manager, - Path: p, + Path: p.Copy(), }) }) } diff --git a/merge/conflict_test.go b/merge/conflict_test.go index f674f87c..685fde98 100644 --- a/merge/conflict_test.go +++ b/merge/conflict_test.go @@ -17,6 +17,7 @@ limitations under the License. package merge_test import ( + "reflect" "testing" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" @@ -92,3 +93,44 @@ func TestToSet(t *testing.T) { t.Fatalf("expected\n%v\n, but got\n%v\n", expected, actual) } } + +func TestConflictsFromManagers(t *testing.T) { + type args struct { + sets fieldpath.ManagedFields + } + tests := []struct { + name string + args args + want string + }{ + { + name: "test with common prefix", + args: args{ + sets: fieldpath.ManagedFields{ + "Bob": fieldpath.NewVersionedSet( + _NS( + _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "livenessProbe", "exec", "command"), + _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "livenessProbe", "periodSeconds"), + _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "readinessProbe", "exec", "command"), + _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "readinessProbe", "periodSeconds"), + ), + "v1", + false, + ), + }, + }, + want: `conflicts with "Bob": +- .spec.template.spec.containers[name="probe"].livenessProbe.periodSeconds +- .spec.template.spec.containers[name="probe"].livenessProbe.exec.command +- .spec.template.spec.containers[name="probe"].readinessProbe.periodSeconds +- .spec.template.spec.containers[name="probe"].readinessProbe.exec.command`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := merge.ConflictsFromManagers(tt.args.sets); !reflect.DeepEqual(got.Error(), tt.want) { + t.Errorf("ConflictsFromManagers() = %v, want %v", got, tt.want) + } + }) + } +} From 6371a4d18e3791106a90b1eaf8e771705d0ec44a Mon Sep 17 00:00:00 2001 From: Keerthan Reddy Mala Date: Fri, 7 Jul 2023 10:07:36 -0700 Subject: [PATCH 045/105] simplify test --- merge/conflict_test.go | 54 +++++++++++++----------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/merge/conflict_test.go b/merge/conflict_test.go index 685fde98..8d816948 100644 --- a/merge/conflict_test.go +++ b/merge/conflict_test.go @@ -17,7 +17,6 @@ limitations under the License. package merge_test import ( - "reflect" "testing" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" @@ -95,42 +94,21 @@ func TestToSet(t *testing.T) { } func TestConflictsFromManagers(t *testing.T) { - type args struct { - sets fieldpath.ManagedFields - } - tests := []struct { - name string - args args - want string - }{ - { - name: "test with common prefix", - args: args{ - sets: fieldpath.ManagedFields{ - "Bob": fieldpath.NewVersionedSet( - _NS( - _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "livenessProbe", "exec", "command"), - _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "livenessProbe", "periodSeconds"), - _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "readinessProbe", "exec", "command"), - _P("spec", "template", "spec", "containers", _KBF("name", "probe"), "readinessProbe", "periodSeconds"), - ), - "v1", - false, - ), - }, - }, - want: `conflicts with "Bob": -- .spec.template.spec.containers[name="probe"].livenessProbe.periodSeconds -- .spec.template.spec.containers[name="probe"].livenessProbe.exec.command -- .spec.template.spec.containers[name="probe"].readinessProbe.periodSeconds -- .spec.template.spec.containers[name="probe"].readinessProbe.exec.command`, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := merge.ConflictsFromManagers(tt.args.sets); !reflect.DeepEqual(got.Error(), tt.want) { - t.Errorf("ConflictsFromManagers() = %v, want %v", got, tt.want) - } - }) + + got := merge.ConflictsFromManagers(fieldpath.ManagedFields{ + "Bob": fieldpath.NewVersionedSet( + _NS( + _P("obj", "template", "obj", "list", _KBF("name", "a"), "id"), + _P("obj", "template", "obj", "list", _KBF("name", "a"), "key"), + ), + "v1", + false, + ), + }) + wanted := `conflicts with "Bob": +- .obj.template.obj.list[name="a"].id +- .obj.template.obj.list[name="a"].key` + if got.Error() != wanted { + t.Errorf("Got %v, wanted %v", got.Error(), wanted) } } From 2265178afe118d9d0f6bf01776dbe82f9a63acdb Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 7 Jul 2023 10:45:27 -0700 Subject: [PATCH 046/105] lavalamp and jennybuckley are emeritus now --- OWNERS | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OWNERS b/OWNERS index c5d34c19..23b0aaa2 100644 --- a/OWNERS +++ b/OWNERS @@ -1,11 +1,10 @@ # See the OWNERS docs: https://go.k8s.io/owners approvers: - - lavalamp - apelisse - - jennybuckley +emeritus_approvers: + - lavalamp # 2023-05-13 + - jennybuckley # 2021-05-13 reviewers: - - lavalamp - apelisse - - jennybuckley - jpbetz From 69bc7e73e77e188b73c3b30dfd2ad62227d3de75 Mon Sep 17 00:00:00 2001 From: Keerthan Reddy Mala Date: Fri, 7 Jul 2023 11:09:31 -0700 Subject: [PATCH 047/105] address review comment --- merge/conflict_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/merge/conflict_test.go b/merge/conflict_test.go index 8d816948..49841856 100644 --- a/merge/conflict_test.go +++ b/merge/conflict_test.go @@ -94,7 +94,6 @@ func TestToSet(t *testing.T) { } func TestConflictsFromManagers(t *testing.T) { - got := merge.ConflictsFromManagers(fieldpath.ManagedFields{ "Bob": fieldpath.NewVersionedSet( _NS( From 479fba6cf99b45320320732acc8c58ab44e5393b Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 16 Aug 2023 15:33:57 -0700 Subject: [PATCH 048/105] typed: Split merge into merge and compare This keeps the code similar and merely splits merge in between merge and compare which will make it easier to modify one without affecting the other. --- typed/compare.go | 401 +++++++++++++++++++++++++++++++++++++++++++++++ typed/typed.go | 55 ++++++- 2 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 typed/compare.go diff --git a/typed/compare.go b/typed/compare.go new file mode 100644 index 00000000..e5066350 --- /dev/null +++ b/typed/compare.go @@ -0,0 +1,401 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 typed + +import ( + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + "sigs.k8s.io/structured-merge-diff/v4/schema" + "sigs.k8s.io/structured-merge-diff/v4/value" +) + +type compareWalker struct { + lhs value.Value + rhs value.Value + schema *schema.Schema + typeRef schema.TypeRef + + // Current path that we are comparing + path fieldpath.Path + + // How to compare. Called after schema validation for all leaf fields. + rule compareRule + + // If set, called after non-leaf items have been compared. (`out` is + // probably already set.) + postItemHook compareRule + + // output of the merge operation (nil if none) + out *interface{} + + // internal housekeeping--don't set when constructing. + inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list + + // Allocate only as many walkers as needed for the depth by storing them here. + spareWalkers *[]*compareWalker + + allocator value.Allocator +} + +// compareRule examine w.lhs and w.rhs (up to one of which may be nil) and +// optionally set w.out. If lhs and rhs are both set, they will be of +// comparable type. +type compareRule func(w *compareWalker) + +// compare compares stuff. +func (w *compareWalker) compare(prefixFn func() string) (errs ValidationErrors) { + if w.lhs == nil && w.rhs == nil { + // check this condidition here instead of everywhere below. + return errorf("at least one of lhs and rhs must be provided") + } + a, ok := w.schema.Resolve(w.typeRef) + if !ok { + return errorf("schema error: no type found matching: %v", *w.typeRef.NamedType) + } + + alhs := deduceAtom(a, w.lhs) + arhs := deduceAtom(a, w.rhs) + + // deduceAtom does not fix the type for nil values + // nil is a wildcard and will accept whatever form the other operand takes + if w.rhs == nil { + errs = append(errs, handleAtom(alhs, w.typeRef, w)...) + } else if w.lhs == nil || alhs.Equals(&arhs) { + errs = append(errs, handleAtom(arhs, w.typeRef, w)...) + } else { + w2 := *w + errs = append(errs, handleAtom(alhs, w.typeRef, &w2)...) + errs = append(errs, handleAtom(arhs, w.typeRef, w)...) + } + + if !w.inLeaf && w.postItemHook != nil { + w.postItemHook(w) + } + return errs.WithLazyPrefix(prefixFn) +} + +// doLeaf should be called on leaves before descending into children, if there +// will be a descent. It modifies w.inLeaf. +func (w *compareWalker) doLeaf() { + if w.inLeaf { + // We're in a "big leaf", an atomic map or list. Ignore + // subsequent leaves. + return + } + w.inLeaf = true + + // We don't recurse into leaf fields for merging. + w.rule(w) +} + +func (w *compareWalker) doScalar(t *schema.Scalar) ValidationErrors { + // Make sure at least one side is a valid scalar. + lerrs := validateScalar(t, w.lhs, "lhs: ") + rerrs := validateScalar(t, w.rhs, "rhs: ") + if len(lerrs) > 0 && len(rerrs) > 0 { + return append(lerrs, rerrs...) + } + + // All scalars are leaf fields. + w.doLeaf() + + return nil +} + +func (w *compareWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *compareWalker { + if w.spareWalkers == nil { + // first descent. + w.spareWalkers = &[]*compareWalker{} + } + var w2 *compareWalker + if n := len(*w.spareWalkers); n > 0 { + w2, *w.spareWalkers = (*w.spareWalkers)[n-1], (*w.spareWalkers)[:n-1] + } else { + w2 = &compareWalker{} + } + *w2 = *w + w2.typeRef = tr + w2.path = append(w2.path, pe) + w2.lhs = nil + w2.rhs = nil + w2.out = nil + return w2 +} + +func (w *compareWalker) finishDescent(w2 *compareWalker) { + // if the descent caused a realloc, ensure that we reuse the buffer + // for the next sibling. + w.path = w2.path[:len(w2.path)-1] + *w.spareWalkers = append(*w.spareWalkers, w2) +} + +func (w *compareWalker) derefMap(prefix string, v value.Value) (value.Map, ValidationErrors) { + if v == nil { + return nil, nil + } + m, err := mapValue(w.allocator, v) + if err != nil { + return nil, errorf("%v: %v", prefix, err) + } + return m, nil +} + +func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (errs ValidationErrors) { + rLen := 0 + if rhs != nil { + rLen = rhs.Length() + } + lLen := 0 + if lhs != nil { + lLen = lhs.Length() + } + outLen := lLen + if outLen < rLen { + outLen = rLen + } + out := make([]interface{}, 0, outLen) + + rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) + errs = append(errs, rhsErrs...) + lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) + errs = append(errs, lhsErrs...) + + sharedOrder := make([]*fieldpath.PathElement, 0, rLen) + for i := range rhsOrder { + pe := &rhsOrder[i] + if _, ok := observedLHS.Get(*pe); ok { + sharedOrder = append(sharedOrder, pe) + } + } + + var nextShared *fieldpath.PathElement + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } + + lLen, rLen = len(lhsOrder), len(rhsOrder) + for lI, rI := 0, 0; lI < lLen || rI < rLen; { + if lI < lLen && rI < rLen { + pe := lhsOrder[lI] + if pe.Equals(rhsOrder[rI]) { + // merge LHS & RHS items + lChild, _ := observedLHS.Get(pe) + rChild, _ := observedRHS.Get(pe) + mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + lI++ + rI++ + + nextShared = nil + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } + continue + } + if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsOrder[lI]) { + // shared item, but not the one we want in this round + lI++ + continue + } + } + if lI < lLen { + pe := lhsOrder[lI] + if _, ok := observedRHS.Get(pe); !ok { + // take LHS item + lChild, _ := observedLHS.Get(pe) + mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + lI++ + continue + } + } + if rI < rLen { + // Take the RHS item, merge with matching LHS item if possible + pe := rhsOrder[rI] + lChild, _ := observedLHS.Get(pe) // may be nil + rChild, _ := observedRHS.Get(pe) + mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) + errs = append(errs, errs...) + if mergeOut != nil { + out = append(out, *mergeOut) + } + rI++ + // Advance nextShared, if we are merging nextShared. + if nextShared != nil && nextShared.Equals(pe) { + nextShared = nil + if len(sharedOrder) > 0 { + nextShared = sharedOrder[0] + sharedOrder = sharedOrder[1:] + } + } + } + } + + if len(out) > 0 { + i := interface{}(out) + w.out = &i + } + + return errs +} + +func (w *compareWalker) indexListPathElements(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { + var errs ValidationErrors + length := 0 + if list != nil { + length = list.Length() + } + observed := fieldpath.MakePathElementValueMap(length) + pes := make([]fieldpath.PathElement, 0, length) + for i := 0; i < length; i++ { + child := list.At(i) + pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + if err != nil { + errs = append(errs, errorf("element %v: %v", i, err.Error())...) + // If we can't construct the path element, we can't + // even report errors deeper in the schema, so bail on + // this element. + continue + } + // Ignore repeated occurences of `pe`. + if _, found := observed.Get(pe); found { + continue + } + observed.Insert(pe, child) + pes = append(pes, pe) + } + return pes, observed, errs +} + +func (w *compareWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) (out *interface{}, errs ValidationErrors) { + w2 := w.prepareDescent(pe, t.ElementType) + w2.lhs = lChild + w2.rhs = rChild + errs = append(errs, w2.compare(pe.String)...) + if w2.out != nil { + out = w2.out + } + w.finishDescent(w2) + return +} + +func (w *compareWalker) derefList(prefix string, v value.Value) (value.List, ValidationErrors) { + if v == nil { + return nil, nil + } + l, err := listValue(w.allocator, v) + if err != nil { + return nil, errorf("%v: %v", prefix, err) + } + return l, nil +} + +func (w *compareWalker) doList(t *schema.List) (errs ValidationErrors) { + lhs, _ := w.derefList("lhs: ", w.lhs) + if lhs != nil { + defer w.allocator.Free(lhs) + } + rhs, _ := w.derefList("rhs: ", w.rhs) + if rhs != nil { + defer w.allocator.Free(rhs) + } + + // If both lhs and rhs are empty/null, treat it as a + // leaf: this helps preserve the empty/null + // distinction. + emptyPromoteToLeaf := (lhs == nil || lhs.Length() == 0) && (rhs == nil || rhs.Length() == 0) + + if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf { + w.doLeaf() + return nil + } + + if lhs == nil && rhs == nil { + return nil + } + + errs = w.visitListItems(t, lhs, rhs) + + return errs +} + +func (w *compareWalker) visitMapItem(t *schema.Map, out map[string]interface{}, key string, lhs, rhs value.Value) (errs ValidationErrors) { + fieldType := t.ElementType + if sf, ok := t.FindField(key); ok { + fieldType = sf.Type + } + pe := fieldpath.PathElement{FieldName: &key} + w2 := w.prepareDescent(pe, fieldType) + w2.lhs = lhs + w2.rhs = rhs + errs = append(errs, w2.compare(pe.String)...) + if w2.out != nil { + out[key] = *w2.out + } + w.finishDescent(w2) + return errs +} + +func (w *compareWalker) visitMapItems(t *schema.Map, lhs, rhs value.Map) (errs ValidationErrors) { + out := map[string]interface{}{} + + value.MapZipUsing(w.allocator, lhs, rhs, value.Unordered, func(key string, lhsValue, rhsValue value.Value) bool { + errs = append(errs, w.visitMapItem(t, out, key, lhsValue, rhsValue)...) + return true + }) + if len(out) > 0 { + i := interface{}(out) + w.out = &i + } + + return errs +} + +func (w *compareWalker) doMap(t *schema.Map) (errs ValidationErrors) { + lhs, _ := w.derefMap("lhs: ", w.lhs) + if lhs != nil { + defer w.allocator.Free(lhs) + } + rhs, _ := w.derefMap("rhs: ", w.rhs) + if rhs != nil { + defer w.allocator.Free(rhs) + } + // If both lhs and rhs are empty/null, treat it as a + // leaf: this helps preserve the empty/null + // distinction. + emptyPromoteToLeaf := (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty()) + + if t.ElementRelationship == schema.Atomic || emptyPromoteToLeaf { + w.doLeaf() + return nil + } + + if lhs == nil && rhs == nil { + return nil + } + + errs = append(errs, w.visitMapItems(t, lhs, rhs)...) + + return errs +} diff --git a/typed/typed.go b/typed/typed.go index 6411bd51..98868176 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -130,7 +130,7 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) { Added: fieldpath.NewSet(), } a := value.NewFreelistAllocator() - _, err = merge(&tv, rhs, func(w *mergingWalker) { + _, err = compare(&tv, rhs, func(w *compareWalker) { if w.lhs == nil { c.Added.Insert(w.path) } else if w.rhs == nil { @@ -140,7 +140,7 @@ func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) { // Need to implement equality check on the value type. c.Modified.Insert(w.path) } - }, func(w *mergingWalker) { + }, func(w *compareWalker) { if w.lhs == nil { c.Added.Insert(w.path) } else if w.rhs == nil { @@ -279,6 +279,57 @@ func merge(lhs, rhs *TypedValue, rule, postRule mergeRule) (*TypedValue, error) return out, nil } +var cmpwPool = sync.Pool{ + New: func() interface{} { return &compareWalker{} }, +} + +func compare(lhs, rhs *TypedValue, rule, postRule compareRule) (*TypedValue, error) { + if lhs.schema != rhs.schema { + return nil, errorf("expected objects with types from the same schema") + } + if !lhs.typeRef.Equals(&rhs.typeRef) { + return nil, errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef) + } + + cmpw := cmpwPool.Get().(*compareWalker) + defer func() { + cmpw.lhs = nil + cmpw.rhs = nil + cmpw.schema = nil + cmpw.typeRef = schema.TypeRef{} + cmpw.rule = nil + cmpw.postItemHook = nil + cmpw.out = nil + cmpw.inLeaf = false + + cmpwPool.Put(cmpw) + }() + + cmpw.lhs = lhs.value + cmpw.rhs = rhs.value + cmpw.schema = lhs.schema + cmpw.typeRef = lhs.typeRef + cmpw.rule = rule + cmpw.postItemHook = postRule + if cmpw.allocator == nil { + cmpw.allocator = value.NewFreelistAllocator() + } + + errs := cmpw.compare(nil) + if len(errs) > 0 { + return nil, errs + } + + out := &TypedValue{ + schema: lhs.schema, + typeRef: lhs.typeRef, + } + if cmpw.out != nil { + out.value = value.NewValueInterface(*cmpw.out) + } + return out, nil +} + // Comparison is the return value of a TypedValue.Compare() operation. // // No field will appear in more than one of the three fieldsets. If all of the From 6703e6d62131475c41d22d3c1b90e5c038ef7176 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 16 Aug 2023 16:01:37 -0700 Subject: [PATCH 049/105] typed: Simplify merge and compare to be more focused Update merge and compare to specifically do what they are supposed to do. This results in some performance improvements and simplifies the code quite significantly. --- typed/compare.go | 242 ++++++++++++++++++++++++----------------------- typed/typed.go | 158 +++++++------------------------ 2 files changed, 158 insertions(+), 242 deletions(-) diff --git a/typed/compare.go b/typed/compare.go index e5066350..40c175e6 100644 --- a/typed/compare.go +++ b/typed/compare.go @@ -17,11 +17,61 @@ limitations under the License. package typed import ( + "fmt" + "strings" + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/schema" "sigs.k8s.io/structured-merge-diff/v4/value" ) +// Comparison is the return value of a TypedValue.Compare() operation. +// +// No field will appear in more than one of the three fieldsets. If all of the +// fieldsets are empty, then the objects must have been equal. +type Comparison struct { + // Removed contains any fields removed by rhs (the right-hand-side + // object in the comparison). + Removed *fieldpath.Set + // Modified contains fields present in both objects but different. + Modified *fieldpath.Set + // Added contains any fields added by rhs. + Added *fieldpath.Set +} + +// IsSame returns true if the comparison returned no changes (the two +// compared objects are similar). +func (c *Comparison) IsSame() bool { + return c.Removed.Empty() && c.Modified.Empty() && c.Added.Empty() +} + +// String returns a human readable version of the comparison. +func (c *Comparison) String() string { + bld := strings.Builder{} + if !c.Modified.Empty() { + bld.WriteString(fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified)) + } + if !c.Added.Empty() { + bld.WriteString(fmt.Sprintf("- Added Fields:\n%v\n", c.Added)) + } + if !c.Removed.Empty() { + bld.WriteString(fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed)) + } + return bld.String() +} + +// ExcludeFields fields from the compare recursively removes the fields +// from the entire comparison +func (c *Comparison) ExcludeFields(fields *fieldpath.Set) *Comparison { + if fields == nil || fields.Empty() { + return c + } + c.Removed = c.Removed.RecursiveDifference(fields) + c.Modified = c.Modified.RecursiveDifference(fields) + c.Added = c.Added.RecursiveDifference(fields) + return c +} + type compareWalker struct { lhs value.Value rhs value.Value @@ -31,15 +81,8 @@ type compareWalker struct { // Current path that we are comparing path fieldpath.Path - // How to compare. Called after schema validation for all leaf fields. - rule compareRule - - // If set, called after non-leaf items have been compared. (`out` is - // probably already set.) - postItemHook compareRule - - // output of the merge operation (nil if none) - out *interface{} + // Resulting comparison. + comparison *Comparison // internal housekeeping--don't set when constructing. inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list @@ -50,11 +93,6 @@ type compareWalker struct { allocator value.Allocator } -// compareRule examine w.lhs and w.rhs (up to one of which may be nil) and -// optionally set w.out. If lhs and rhs are both set, they will be of -// comparable type. -type compareRule func(w *compareWalker) - // compare compares stuff. func (w *compareWalker) compare(prefixFn func() string) (errs ValidationErrors) { if w.lhs == nil && w.rhs == nil { @@ -81,8 +119,12 @@ func (w *compareWalker) compare(prefixFn func() string) (errs ValidationErrors) errs = append(errs, handleAtom(arhs, w.typeRef, w)...) } - if !w.inLeaf && w.postItemHook != nil { - w.postItemHook(w) + if !w.inLeaf { + if w.lhs == nil { + w.comparison.Added.Insert(w.path) + } else if w.rhs == nil { + w.comparison.Removed.Insert(w.path) + } } return errs.WithLazyPrefix(prefixFn) } @@ -98,7 +140,15 @@ func (w *compareWalker) doLeaf() { w.inLeaf = true // We don't recurse into leaf fields for merging. - w.rule(w) + if w.lhs == nil { + w.comparison.Added.Insert(w.path) + } else if w.rhs == nil { + w.comparison.Removed.Insert(w.path) + } else if !value.EqualsUsing(w.allocator, w.rhs, w.lhs) { + // TODO: Equality is not sufficient for this. + // Need to implement equality check on the value type. + w.comparison.Modified.Insert(w.path) + } } func (w *compareWalker) doScalar(t *schema.Scalar) ValidationErrors { @@ -115,7 +165,7 @@ func (w *compareWalker) doScalar(t *schema.Scalar) ValidationErrors { return nil } -func (w *compareWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *compareWalker { +func (w *compareWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef, cmp *Comparison) *compareWalker { if w.spareWalkers == nil { // first descent. w.spareWalkers = &[]*compareWalker{} @@ -131,7 +181,7 @@ func (w *compareWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeR w2.path = append(w2.path, pe) w2.lhs = nil w2.rhs = nil - w2.out = nil + w2.comparison = cmp return w2 } @@ -162,102 +212,64 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if lhs != nil { lLen = lhs.Length() } - outLen := lLen - if outLen < rLen { - outLen = rLen - } - out := make([]interface{}, 0, outLen) - - rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) - errs = append(errs, rhsErrs...) - lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) - errs = append(errs, lhsErrs...) - sharedOrder := make([]*fieldpath.PathElement, 0, rLen) - for i := range rhsOrder { - pe := &rhsOrder[i] - if _, ok := observedLHS.Get(*pe); ok { - sharedOrder = append(sharedOrder, pe) + lValues := fieldpath.MakePathElementValueMap(lLen) + for i := 0; i < lLen; i++ { + child := lhs.At(i) + pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + if err != nil { + errs = append(errs, errorf("element %v: %v", i, err.Error())...) + // If we can't construct the path element, we can't + // even report errors deeper in the schema, so bail on + // this element. + continue + } + // Ignore repeated occurences of `pe`. + if _, found := lValues.Get(pe); found { + continue } + lValues.Insert(pe, child) } - var nextShared *fieldpath.PathElement - if len(sharedOrder) > 0 { - nextShared = sharedOrder[0] - sharedOrder = sharedOrder[1:] - } - - lLen, rLen = len(lhsOrder), len(rhsOrder) - for lI, rI := 0, 0; lI < lLen || rI < rLen; { - if lI < lLen && rI < rLen { - pe := lhsOrder[lI] - if pe.Equals(rhsOrder[rI]) { - // merge LHS & RHS items - lChild, _ := observedLHS.Get(pe) - rChild, _ := observedRHS.Get(pe) - mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) - errs = append(errs, errs...) - if mergeOut != nil { - out = append(out, *mergeOut) - } - lI++ - rI++ - - nextShared = nil - if len(sharedOrder) > 0 { - nextShared = sharedOrder[0] - sharedOrder = sharedOrder[1:] - } - continue - } - if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsOrder[lI]) { - // shared item, but not the one we want in this round - lI++ - continue - } - } - if lI < lLen { - pe := lhsOrder[lI] - if _, ok := observedRHS.Get(pe); !ok { - // take LHS item - lChild, _ := observedLHS.Get(pe) - mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) - errs = append(errs, errs...) - if mergeOut != nil { - out = append(out, *mergeOut) - } - lI++ - continue - } + rValues := fieldpath.MakePathElementValueMap(rLen) + for i := 0; i < rLen; i++ { + rValue := rhs.At(i) + pe, err := listItemToPathElement(w.allocator, w.schema, t, i, rValue) + if err != nil { + errs = append(errs, errorf("element %v: %v", i, err.Error())...) + // If we can't construct the path element, we can't + // even report errors deeper in the schema, so bail on + // this element. + continue } - if rI < rLen { - // Take the RHS item, merge with matching LHS item if possible - pe := rhsOrder[rI] - lChild, _ := observedLHS.Get(pe) // may be nil - rChild, _ := observedRHS.Get(pe) - mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) - errs = append(errs, errs...) - if mergeOut != nil { - out = append(out, *mergeOut) - } - rI++ - // Advance nextShared, if we are merging nextShared. - if nextShared != nil && nextShared.Equals(pe) { - nextShared = nil - if len(sharedOrder) > 0 { - nextShared = sharedOrder[0] - sharedOrder = sharedOrder[1:] - } - } + // Ignore repeated occurences of `pe`. + if _, found := rValues.Get(pe); found { + continue } + rValues.Insert(pe, rValue) + // We can merge with nil if lValue is not present. + lValue, _ := lValues.Get(pe) + errs = append(errs, w.mergeListItem(t, pe, lValue, rValue)...) } - if len(out) > 0 { - i := interface{}(out) - w.out = &i + // Add items from left that are not in right. + for i := 0; i < lLen; i++ { + lValue := lhs.At(i) + pe, err := listItemToPathElement(w.allocator, w.schema, t, i, lValue) + if err != nil { + errs = append(errs, errorf("element %v: %v", i, err.Error())...) + // If we can't construct the path element, we can't + // even report errors deeper in the schema, so bail on + // this element. + continue + } + if _, found := rValues.Get(pe); found { + continue + } + errs = append(errs, w.mergeListItem(t, pe, lValue, nil)...) } - return errs + return } func (w *compareWalker) indexListPathElements(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { @@ -288,16 +300,13 @@ func (w *compareWalker) indexListPathElements(t *schema.List, list value.List) ( return pes, observed, errs } -func (w *compareWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) (out *interface{}, errs ValidationErrors) { - w2 := w.prepareDescent(pe, t.ElementType) +func (w *compareWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) ValidationErrors { + w2 := w.prepareDescent(pe, t.ElementType, w.comparison) w2.lhs = lChild w2.rhs = rChild - errs = append(errs, w2.compare(pe.String)...) - if w2.out != nil { - out = w2.out - } + errs := w2.compare(pe.String) w.finishDescent(w2) - return + return errs } func (w *compareWalker) derefList(prefix string, v value.Value) (value.List, ValidationErrors) { @@ -346,13 +355,10 @@ func (w *compareWalker) visitMapItem(t *schema.Map, out map[string]interface{}, fieldType = sf.Type } pe := fieldpath.PathElement{FieldName: &key} - w2 := w.prepareDescent(pe, fieldType) + w2 := w.prepareDescent(pe, fieldType, w.comparison) w2.lhs = lhs w2.rhs = rhs errs = append(errs, w2.compare(pe.String)...) - if w2.out != nil { - out[key] = *w2.out - } w.finishDescent(w2) return errs } @@ -364,10 +370,6 @@ func (w *compareWalker) visitMapItems(t *schema.Map, lhs, rhs value.Map) (errs V errs = append(errs, w.visitMapItem(t, out, key, lhsValue, rhsValue)...) return true }) - if len(out) > 0 { - i := interface{}(out) - w.out = &i - } return errs } diff --git a/typed/typed.go b/typed/typed.go index 98868176..8c26bc3d 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -17,8 +17,6 @@ limitations under the License. package typed import ( - "fmt" - "strings" "sync" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" @@ -117,6 +115,10 @@ func (tv TypedValue) Merge(pso *TypedValue) (*TypedValue, error) { return merge(&tv, pso, ruleKeepRHS, nil) } +var cmpwPool = sync.Pool{ + New: func() interface{} { return &compareWalker{} }, +} + // Compare compares the two objects. See the comments on the `Comparison` // struct for details on the return value. // @@ -124,34 +126,44 @@ func (tv TypedValue) Merge(pso *TypedValue) (*TypedValue, error) { // match), or an error will be returned. Validation errors will be returned if // the objects don't conform to the schema. func (tv TypedValue) Compare(rhs *TypedValue) (c *Comparison, err error) { - c = &Comparison{ + lhs := tv + if lhs.schema != rhs.schema { + return nil, errorf("expected objects with types from the same schema") + } + if !lhs.typeRef.Equals(&rhs.typeRef) { + return nil, errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef) + } + + cmpw := cmpwPool.Get().(*compareWalker) + defer func() { + cmpw.lhs = nil + cmpw.rhs = nil + cmpw.schema = nil + cmpw.typeRef = schema.TypeRef{} + cmpw.comparison = nil + cmpw.inLeaf = false + + cmpwPool.Put(cmpw) + }() + + cmpw.lhs = lhs.value + cmpw.rhs = rhs.value + cmpw.schema = lhs.schema + cmpw.typeRef = lhs.typeRef + cmpw.comparison = &Comparison{ Removed: fieldpath.NewSet(), Modified: fieldpath.NewSet(), Added: fieldpath.NewSet(), } - a := value.NewFreelistAllocator() - _, err = compare(&tv, rhs, func(w *compareWalker) { - if w.lhs == nil { - c.Added.Insert(w.path) - } else if w.rhs == nil { - c.Removed.Insert(w.path) - } else if !value.EqualsUsing(a, w.rhs, w.lhs) { - // TODO: Equality is not sufficient for this. - // Need to implement equality check on the value type. - c.Modified.Insert(w.path) - } - }, func(w *compareWalker) { - if w.lhs == nil { - c.Added.Insert(w.path) - } else if w.rhs == nil { - c.Removed.Insert(w.path) - } - }) - if err != nil { - return nil, err + if cmpw.allocator == nil { + cmpw.allocator = value.NewFreelistAllocator() } - return c, nil + errs := cmpw.compare(nil) + if len(errs) > 0 { + return nil, errs + } + return cmpw.comparison, nil } // RemoveItems removes each provided list or map item from the value. @@ -278,101 +290,3 @@ func merge(lhs, rhs *TypedValue, rule, postRule mergeRule) (*TypedValue, error) } return out, nil } - -var cmpwPool = sync.Pool{ - New: func() interface{} { return &compareWalker{} }, -} - -func compare(lhs, rhs *TypedValue, rule, postRule compareRule) (*TypedValue, error) { - if lhs.schema != rhs.schema { - return nil, errorf("expected objects with types from the same schema") - } - if !lhs.typeRef.Equals(&rhs.typeRef) { - return nil, errorf("expected objects of the same type, but got %v and %v", lhs.typeRef, rhs.typeRef) - } - - cmpw := cmpwPool.Get().(*compareWalker) - defer func() { - cmpw.lhs = nil - cmpw.rhs = nil - cmpw.schema = nil - cmpw.typeRef = schema.TypeRef{} - cmpw.rule = nil - cmpw.postItemHook = nil - cmpw.out = nil - cmpw.inLeaf = false - - cmpwPool.Put(cmpw) - }() - - cmpw.lhs = lhs.value - cmpw.rhs = rhs.value - cmpw.schema = lhs.schema - cmpw.typeRef = lhs.typeRef - cmpw.rule = rule - cmpw.postItemHook = postRule - if cmpw.allocator == nil { - cmpw.allocator = value.NewFreelistAllocator() - } - - errs := cmpw.compare(nil) - if len(errs) > 0 { - return nil, errs - } - - out := &TypedValue{ - schema: lhs.schema, - typeRef: lhs.typeRef, - } - if cmpw.out != nil { - out.value = value.NewValueInterface(*cmpw.out) - } - return out, nil -} - -// Comparison is the return value of a TypedValue.Compare() operation. -// -// No field will appear in more than one of the three fieldsets. If all of the -// fieldsets are empty, then the objects must have been equal. -type Comparison struct { - // Removed contains any fields removed by rhs (the right-hand-side - // object in the comparison). - Removed *fieldpath.Set - // Modified contains fields present in both objects but different. - Modified *fieldpath.Set - // Added contains any fields added by rhs. - Added *fieldpath.Set -} - -// IsSame returns true if the comparison returned no changes (the two -// compared objects are similar). -func (c *Comparison) IsSame() bool { - return c.Removed.Empty() && c.Modified.Empty() && c.Added.Empty() -} - -// String returns a human readable version of the comparison. -func (c *Comparison) String() string { - bld := strings.Builder{} - if !c.Modified.Empty() { - bld.WriteString(fmt.Sprintf("- Modified Fields:\n%v\n", c.Modified)) - } - if !c.Added.Empty() { - bld.WriteString(fmt.Sprintf("- Added Fields:\n%v\n", c.Added)) - } - if !c.Removed.Empty() { - bld.WriteString(fmt.Sprintf("- Removed Fields:\n%v\n", c.Removed)) - } - return bld.String() -} - -// ExcludeFields fields from the compare recursively removes the fields -// from the entire comparison -func (c *Comparison) ExcludeFields(fields *fieldpath.Set) *Comparison { - if fields == nil || fields.Empty() { - return c - } - c.Removed = c.Removed.RecursiveDifference(fields) - c.Modified = c.Modified.RecursiveDifference(fields) - c.Added = c.Added.RecursiveDifference(fields) - return c -} From 8f90031a7f0edcc3442fadda5bd4e189ea38629c Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 7 Sep 2023 14:31:13 -0700 Subject: [PATCH 050/105] Include mandatory atomic relation for lists in all schemas --- internal/testdata/schema.yaml | 2 +- schema/schemaschema.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/testdata/schema.yaml b/internal/testdata/schema.yaml index e85de20a..789d6c6e 100644 --- a/internal/testdata/schema.yaml +++ b/internal/testdata/schema.yaml @@ -87,10 +87,10 @@ types: list: elementType: scalar: string + elementRelationship: atomic - name: untyped map: fields: - name: elementRelationship type: scalar: string - diff --git a/schema/schemaschema.go b/schema/schemaschema.go index e4c5caa2..6eb6c36d 100644 --- a/schema/schemaschema.go +++ b/schema/schemaschema.go @@ -145,6 +145,7 @@ var SchemaSchemaYAML = `types: list: elementType: scalar: string + elementRelationship: atomic - name: untyped map: fields: From 69b4451bdba8a99c172ef9a4861b076251f8d9bc Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 7 Sep 2023 14:39:39 -0700 Subject: [PATCH 051/105] Remove index from internal API/function calls --- typed/compare.go | 8 ++++---- typed/helpers.go | 21 ++++++++++----------- typed/merge.go | 2 +- typed/remove.go | 4 ++-- typed/tofieldset.go | 2 +- typed/validate.go | 2 +- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/typed/compare.go b/typed/compare.go index 40c175e6..5c3ffc00 100644 --- a/typed/compare.go +++ b/typed/compare.go @@ -216,7 +216,7 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err lValues := fieldpath.MakePathElementValueMap(lLen) for i := 0; i < lLen; i++ { child := lhs.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + pe, err := listItemToPathElement(w.allocator, w.schema, t, child) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't @@ -234,7 +234,7 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err rValues := fieldpath.MakePathElementValueMap(rLen) for i := 0; i < rLen; i++ { rValue := rhs.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, rValue) + pe, err := listItemToPathElement(w.allocator, w.schema, t, rValue) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't @@ -255,7 +255,7 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // Add items from left that are not in right. for i := 0; i < lLen; i++ { lValue := lhs.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, lValue) + pe, err := listItemToPathElement(w.allocator, w.schema, t, lValue) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't @@ -282,7 +282,7 @@ func (w *compareWalker) indexListPathElements(t *schema.List, list value.List) ( pes := make([]fieldpath.PathElement, 0, length) for i := 0; i < length; i++ { child := list.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + pe, err := listItemToPathElement(w.allocator, w.schema, t, child) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't diff --git a/typed/helpers.go b/typed/helpers.go index 19c77334..78fdb0e7 100644 --- a/typed/helpers.go +++ b/typed/helpers.go @@ -197,7 +197,7 @@ func getAssociativeKeyDefault(s *schema.Schema, list *schema.List, fieldName str return field.Default, nil } -func keyedAssociativeListItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) { +func keyedAssociativeListItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, child value.Value) (fieldpath.PathElement, error) { pe := fieldpath.PathElement{} if child.IsNull() { // null entries are illegal. @@ -225,7 +225,7 @@ func keyedAssociativeListItemToPathElement(a value.Allocator, s *schema.Schema, return pe, nil } -func setItemToPathElement(list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) { +func setItemToPathElement(child value.Value) (fieldpath.PathElement, error) { pe := fieldpath.PathElement{} switch { case child.IsMap(): @@ -245,16 +245,15 @@ func setItemToPathElement(list *schema.List, index int, child value.Value) (fiel } } -func listItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, index int, child value.Value) (fieldpath.PathElement, error) { - if list.ElementRelationship == schema.Associative { - if len(list.Keys) > 0 { - return keyedAssociativeListItemToPathElement(a, s, list, index, child) - } +func listItemToPathElement(a value.Allocator, s *schema.Schema, list *schema.List, child value.Value) (fieldpath.PathElement, error) { + if list.ElementRelationship != schema.Associative { + return fieldpath.PathElement{}, errors.New("invalid indexing of non-associative list") + } - // If there's no keys, then we must be a set of primitives. - return setItemToPathElement(list, index, child) + if len(list.Keys) > 0 { + return keyedAssociativeListItemToPathElement(a, s, list, child) } - // Use the index as a key for atomic lists. - return fieldpath.PathElement{Index: &index}, nil + // If there's no keys, then we must be a set of primitives. + return setItemToPathElement(child) } diff --git a/typed/merge.go b/typed/merge.go index 09209ec8..e5d8ec0e 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -282,7 +282,7 @@ func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List) ( pes := make([]fieldpath.PathElement, 0, length) for i := 0; i < length; i++ { child := list.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, i, child) + pe, err := listItemToPathElement(w.allocator, w.schema, t, child) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't diff --git a/typed/remove.go b/typed/remove.go index a338d761..ad071ee8 100644 --- a/typed/remove.go +++ b/typed/remove.go @@ -74,9 +74,9 @@ func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) { iter := l.RangeUsing(w.allocator) defer w.allocator.Free(iter) for iter.Next() { - i, item := iter.Item() + _, item := iter.Item() // Ignore error because we have already validated this list - pe, _ := listItemToPathElement(w.allocator, w.schema, t, i, item) + pe, _ := listItemToPathElement(w.allocator, w.schema, t, item) path, _ := fieldpath.MakePath(pe) // save items on the path when we shouldExtract // but ignore them when we are removing (i.e. !w.shouldExtract) diff --git a/typed/tofieldset.go b/typed/tofieldset.go index 047efff0..0ce007fc 100644 --- a/typed/tofieldset.go +++ b/typed/tofieldset.go @@ -96,7 +96,7 @@ func (v *toFieldSetWalker) doScalar(t *schema.Scalar) ValidationErrors { func (v *toFieldSetWalker) visitListItems(t *schema.List, list value.List) (errs ValidationErrors) { for i := 0; i < list.Length(); i++ { child := list.At(i) - pe, _ := listItemToPathElement(v.allocator, v.schema, t, i, child) + pe, _ := listItemToPathElement(v.allocator, v.schema, t, child) v2 := v.prepareDescent(pe, t.ElementType) v2.value = child errs = append(errs, v2.toFieldSet()...) diff --git a/typed/validate.go b/typed/validate.go index edddbafa..ee8c2f28 100644 --- a/typed/validate.go +++ b/typed/validate.go @@ -129,7 +129,7 @@ func (v *validatingObjectWalker) visitListItems(t *schema.List, list value.List) pe.Index = &i } else { var err error - pe, err = listItemToPathElement(v.allocator, v.schema, t, i, child) + pe, err = listItemToPathElement(v.allocator, v.schema, t, child) if err != nil { errs = append(errs, errorf("element %v: %v", i, err.Error())...) // If we can't construct the path element, we can't From 4d85fbb49edcf1c34facbf3f2e8d44a7624b2b81 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 7 Sep 2023 14:40:21 -0700 Subject: [PATCH 052/105] Further rename merge -> compare in compare.go --- typed/compare.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/typed/compare.go b/typed/compare.go index 5c3ffc00..6fb398ae 100644 --- a/typed/compare.go +++ b/typed/compare.go @@ -247,9 +247,9 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err continue } rValues.Insert(pe, rValue) - // We can merge with nil if lValue is not present. + // We can compare with nil if lValue is not present. lValue, _ := lValues.Get(pe) - errs = append(errs, w.mergeListItem(t, pe, lValue, rValue)...) + errs = append(errs, w.compareListItem(t, pe, lValue, rValue)...) } // Add items from left that are not in right. @@ -266,7 +266,7 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if _, found := rValues.Get(pe); found { continue } - errs = append(errs, w.mergeListItem(t, pe, lValue, nil)...) + errs = append(errs, w.compareListItem(t, pe, lValue, nil)...) } return @@ -300,7 +300,7 @@ func (w *compareWalker) indexListPathElements(t *schema.List, list value.List) ( return pes, observed, errs } -func (w *compareWalker) mergeListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) ValidationErrors { +func (w *compareWalker) compareListItem(t *schema.List, pe fieldpath.PathElement, lChild, rChild value.Value) ValidationErrors { w2 := w.prepareDescent(pe, t.ElementType, w.comparison) w2.lhs = lChild w2.rhs = rChild From db12191be628d82377c604306a770172cb4661d1 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 14 Jun 2023 15:13:14 -0700 Subject: [PATCH 053/105] validation: Allow loose validation for sets If a set (or associative lists keys) has duplicated elements, then this will not fail the validation. Everything is still the same. --- typed/parser.go | 12 ++++++------ typed/typed.go | 20 +++++++++++++++++--- typed/validate.go | 6 +++++- typed/validate_test.go | 34 ++++++++++++++++++++++++++-------- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/typed/parser.go b/typed/parser.go index 3949a78f..4258ee5b 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -93,13 +93,13 @@ func (p ParseableType) IsValid() bool { // FromYAML parses a yaml string into an object with the current schema // and the type "typename" or an error if validation fails. -func (p ParseableType) FromYAML(object YAMLObject) (*TypedValue, error) { +func (p ParseableType) FromYAML(object YAMLObject, opts ...ValidationOptions) (*TypedValue, error) { var v interface{} err := yaml.Unmarshal([]byte(object), &v) if err != nil { return nil, err } - return AsTyped(value.NewValueInterface(v), p.Schema, p.TypeRef) + return AsTyped(value.NewValueInterface(v), p.Schema, p.TypeRef, opts...) } // FromUnstructured converts a go "interface{}" type, typically an @@ -108,8 +108,8 @@ func (p ParseableType) FromYAML(object YAMLObject) (*TypedValue, error) { // The provided interface{} must be one of: map[string]interface{}, // map[interface{}]interface{}, []interface{}, int types, float types, // string or boolean. Nested interface{} must also be one of these types. -func (p ParseableType) FromUnstructured(in interface{}) (*TypedValue, error) { - return AsTyped(value.NewValueInterface(in), p.Schema, p.TypeRef) +func (p ParseableType) FromUnstructured(in interface{}, opts ...ValidationOptions) (*TypedValue, error) { + return AsTyped(value.NewValueInterface(in), p.Schema, p.TypeRef, opts...) } // FromStructured converts a go "interface{}" type, typically an structured object in @@ -117,12 +117,12 @@ func (p ParseableType) FromUnstructured(in interface{}) (*TypedValue, error) { // schema validation. The provided "interface{}" value must be a pointer so that the // value can be modified via reflection. The provided "interface{}" may contain structs // and types that are converted to Values by the jsonMarshaler interface. -func (p ParseableType) FromStructured(in interface{}) (*TypedValue, error) { +func (p ParseableType) FromStructured(in interface{}, opts ...ValidationOptions) (*TypedValue, error) { v, err := value.NewValueReflect(in) if err != nil { return nil, fmt.Errorf("error creating struct value reflector: %v", err) } - return AsTyped(v, p.Schema, p.TypeRef) + return AsTyped(v, p.Schema, p.TypeRef, opts...) } // DeducedParseableType is a ParseableType that deduces the type from diff --git a/typed/typed.go b/typed/typed.go index 8c26bc3d..035c14bd 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -24,16 +24,24 @@ import ( "sigs.k8s.io/structured-merge-diff/v4/value" ) +// ValidationOptions is the list of all the options available when running the validation. +type ValidationOptions int + +const ( + // AllowDuplicates means that sets and associative lists can have duplicate similar items. + AllowDuplicates ValidationOptions = iota +) + // AsTyped accepts a value and a type and returns a TypedValue. 'v' must have // type 'typeName' in the schema. An error is returned if the v doesn't conform // to the schema. -func AsTyped(v value.Value, s *schema.Schema, typeRef schema.TypeRef) (*TypedValue, error) { +func AsTyped(v value.Value, s *schema.Schema, typeRef schema.TypeRef, opts ...ValidationOptions) (*TypedValue, error) { tv := &TypedValue{ value: v, typeRef: typeRef, schema: s, } - if err := tv.Validate(); err != nil { + if err := tv.Validate(opts...); err != nil { return nil, err } return tv, nil @@ -79,8 +87,14 @@ func (tv TypedValue) Schema() *schema.Schema { } // Validate returns an error with a list of every spec violation. -func (tv TypedValue) Validate() error { +func (tv TypedValue) Validate(opts ...ValidationOptions) error { w := tv.walker() + for _, opt := range opts { + switch opt { + case AllowDuplicates: + w.allowDuplicates = true + } + } defer w.finished() if errs := w.validate(nil); len(errs) != 0 { return errs diff --git a/typed/validate.go b/typed/validate.go index ee8c2f28..652e24c8 100644 --- a/typed/validate.go +++ b/typed/validate.go @@ -33,6 +33,7 @@ func (tv TypedValue) walker() *validatingObjectWalker { v.value = tv.value v.schema = tv.schema v.typeRef = tv.typeRef + v.allowDuplicates = false if v.allocator == nil { v.allocator = value.NewFreelistAllocator() } @@ -49,6 +50,9 @@ type validatingObjectWalker struct { value value.Value schema *schema.Schema typeRef schema.TypeRef + // If set to true, duplicates will be allowed in + // associativeLists/sets. + allowDuplicates bool // Allocate only as many walkers as needed for the depth by storing them here. spareWalkers *[]*validatingObjectWalker @@ -137,7 +141,7 @@ func (v *validatingObjectWalker) visitListItems(t *schema.List, list value.List) // this element. return } - if observedKeys.Has(pe) { + if observedKeys.Has(pe) && !v.allowDuplicates { errs = append(errs, errorf("duplicate entries for key %v", pe.String())...) } observedKeys.Insert(pe) diff --git a/typed/validate_test.go b/typed/validate_test.go index 43357c5c..2eb0041e 100644 --- a/typed/validate_test.go +++ b/typed/validate_test.go @@ -31,6 +31,8 @@ type validationTestCase struct { schema typed.YAMLObject validObjects []typed.YAMLObject invalidObjects []typed.YAMLObject + // duplicatesObjects are valid with AllowDuplicates validation, invalid otherwise. + duplicatesObjects []typed.YAMLObject } var validationCases = []validationTestCase{{ @@ -63,7 +65,6 @@ var validationCases = []validationTestCase{{ `{"key":"foo","value":null}`, `{"key":"foo"}`, `{"key":"foo","value":true}`, - `{"key":"foo","value":true}`, `{"key":null}`, }, invalidObjects: []typed.YAMLObject{ @@ -136,28 +137,27 @@ var validationCases = []validationTestCase{{ `{"bool":"aoeu"}`, `{"bool":{"a":1}}`, `{"bool":["foo"]}`, - `{"setStr":["a","a"]}`, - `{"setBool":[true,false,true]}`, - `{"setNumeric":[1,2,3,3.14159,1]}`, `{"setStr":[1]}`, `{"setStr":[true]}`, `{"setStr":[1.5]}`, `{"setStr":[null]}`, `{"setStr":[{}]}`, `{"setStr":[[]]}`, - `{"setBool":[true,false,true]}`, `{"setBool":[1]}`, `{"setBool":[1.5]}`, `{"setBool":[null]}`, `{"setBool":[{}]}`, `{"setBool":[[]]}`, `{"setBool":["a"]}`, - `{"setNumeric":[1,2,3,3.14159,1]}`, `{"setNumeric":[null]}`, `{"setNumeric":[true]}`, `{"setNumeric":["a"]}`, `{"setNumeric":[[]]}`, `{"setNumeric":[{}]}`, + }, duplicatesObjects: []typed.YAMLObject{ + `{"setStr":["a","a"]}`, + `{"setBool":[true,false,true]}`, + `{"setNumeric":[1,2,3,3.14159,1]}`, }, }, { name: "associative list", @@ -232,9 +232,10 @@ var validationCases = []validationTestCase{{ `{"list":[{}]}`, `{"list":[{"value":{"a":"a"},"bv":true,"nv":3.14}]}`, `{"list":[{"key":"a","id":1,"value":{"a":1}}]}`, - `{"list":[{"key":"a","id":1},{"key":"a","id":1}]}`, `{"list":[{"key":"a","id":1,"value":{"a":"a"},"bv":"true","nv":3.14}]}`, `{"list":[{"key":"a","id":1,"value":{"a":"a"},"bv":true,"nv":false}]}`, + }, duplicatesObjects: []typed.YAMLObject{ + `{"list":[{"key":"a","id":1},{"key":"a","id":1}]}`, }, }} @@ -262,13 +263,30 @@ func (tt validationTestCase) test(t *testing.T) { t.Parallel() _, err := pt.FromYAML(iv) if err == nil { - t.Errorf("Object should fail: %v\n%v", err, iv) + t.Fatalf("Object should fail:\n%v", iv) } if strings.Contains(err.Error(), "invalid atom") { t.Errorf("Error should be useful, but got: %v\n%v", err, iv) } }) } + for i, iv := range tt.duplicatesObjects { + iv := iv + t.Run(fmt.Sprintf("%v-duplicates-%v", tt.name, i), func(t *testing.T) { + t.Parallel() + _, err := pt.FromYAML(iv) + if err == nil { + t.Fatalf("Object should fail:\n%v", iv) + } + if strings.Contains(err.Error(), "invalid atom") { + t.Errorf("Error should be useful, but got: %v\n%v", err, iv) + } + _, err = pt.FromYAML(iv, typed.AllowDuplicates) + if err != nil { + t.Errorf("failed to parse/validate yaml: %v\n%v", err, iv) + } + }) + } } func TestSchemaValidation(t *testing.T) { From 0e499889796f8470118c9cb3907dc52761895dd6 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 15 Jun 2023 16:32:11 -0700 Subject: [PATCH 054/105] tofieldset: Add tests to show it already allow duplicates --- typed/tofieldset.go | 22 ++++++++++++++++++++++ typed/toset_test.go | 21 ++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/typed/tofieldset.go b/typed/tofieldset.go index 0ce007fc..d563a87e 100644 --- a/typed/tofieldset.go +++ b/typed/tofieldset.go @@ -94,9 +94,31 @@ func (v *toFieldSetWalker) doScalar(t *schema.Scalar) ValidationErrors { } func (v *toFieldSetWalker) visitListItems(t *schema.List, list value.List) (errs ValidationErrors) { + // Keeps track of the PEs we've seen + seen := fieldpath.MakePathElementSet(list.Length()) + // Keeps tracks of the PEs we've counted as duplicates + duplicates := fieldpath.MakePathElementSet(list.Length()) for i := 0; i < list.Length(); i++ { child := list.At(i) pe, _ := listItemToPathElement(v.allocator, v.schema, t, child) + if seen.Has(pe) { + if duplicates.Has(pe) { + // do nothing + } else { + v.set.Insert(append(v.path, pe)) + duplicates.Insert(pe) + } + } else { + seen.Insert(pe) + } + } + + for i := 0; i < list.Length(); i++ { + child := list.At(i) + pe, _ := listItemToPathElement(v.allocator, v.schema, t, child) + if duplicates.Has(pe) { + continue + } v2 := v.prepareDescent(pe, t.ElementType) v2.value = child errs = append(errs, v2.toFieldSet()...) diff --git a/typed/toset_test.go b/typed/toset_test.go index 2cc6810c..1b5d4f25 100644 --- a/typed/toset_test.go +++ b/typed/toset_test.go @@ -155,7 +155,12 @@ var fieldsetCases = []fieldsetTestCase{{ _P("setStr", _V("b")), _P("setStr", _V("c")), )}, - {`{"setBool":[true,false]}`, _NS( + {`{"setStr":["a","b","c","a","b","c","c"]}`, _NS( + _P("setStr", _V("a")), + _P("setStr", _V("b")), + _P("setStr", _V("c")), + )}, + {`{"setBool":[true,false,true]}`, _NS( _P("setBool", _V(true)), _P("setBool", _V(false)), )}, @@ -244,6 +249,16 @@ var fieldsetCases = []fieldsetTestCase{{ _P("list", _KBF("key", "b", "id", 1), "key"), _P("list", _KBF("key", "b", "id", 1), "id"), )}, + {`{"list":[{"key":"a","id":1,"nv":2},{"key":"a","id":2,"nv":3},{"key":"b","id":1},{"key":"a","id":2,"bv":true}]}`, _NS( + _P("list", _KBF("key", "a", "id", 1)), + _P("list", _KBF("key", "a", "id", 1), "key"), + _P("list", _KBF("key", "a", "id", 1), "id"), + _P("list", _KBF("key", "a", "id", 1), "nv"), + _P("list", _KBF("key", "a", "id", 2)), + _P("list", _KBF("key", "b", "id", 1)), + _P("list", _KBF("key", "b", "id", 1), "key"), + _P("list", _KBF("key", "b", "id", 1), "id"), + )}, {`{"atomicList":["a","a","a"]}`, _NS(_P("atomicList"))}, }, }} @@ -257,9 +272,9 @@ func (tt fieldsetTestCase) test(t *testing.T) { v := v t.Run(fmt.Sprintf("%v-%v", tt.name, i), func(t *testing.T) { t.Parallel() - tv, err := parser.Type(tt.rootTypeName).FromYAML(v.object) + tv, err := parser.Type(tt.rootTypeName).FromYAML(v.object, typed.AllowDuplicates) if err != nil { - t.Errorf("failed to parse object: %v", err) + t.Fatalf("failed to parse object: %v", err) } fs, err := tv.ToFieldSet() if err != nil { From af4df06508b2558e9c3c34197a8c0a7fcfbdeccb Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 6 Oct 2023 14:07:46 -0700 Subject: [PATCH 055/105] fieldpath: Allow map to be updated --- fieldpath/pathelementmap.go | 2 ++ fieldpath/pathelementmap_test.go | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index 9b14ca58..2611a228 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -53,6 +53,7 @@ func (spev sortedPathElementValues) Less(i, j int) bool { func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], spev[i] } // Insert adds the pathelement and associated value in the map. +// If insert is called twice with the same PathElement, the value is replaced. func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) @@ -62,6 +63,7 @@ func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { return } if s.members[loc].PathElement.Equals(pe) { + s.members[loc].Value = v return } s.members = append(s.members, pathElementValue{}) diff --git a/fieldpath/pathelementmap_test.go b/fieldpath/pathelementmap_test.go index cbabf50f..5685c7ca 100644 --- a/fieldpath/pathelementmap_test.go +++ b/fieldpath/pathelementmap_test.go @@ -47,4 +47,11 @@ func TestPathElementValueMap(t *testing.T) { } else if !value.Equals(val, value.NewValueInterface(2)) { t.Fatalf("Unexpected value found: %#v", val) } + + m.Insert(PathElement{FieldName: strptr("carrot")}, value.NewValueInterface("fork")) + if val, ok := m.Get(PathElement{FieldName: strptr("carrot")}); !ok { + t.Fatal("Missing path-element in map") + } else if !value.Equals(val, value.NewValueInterface("fork")) { + t.Fatalf("Unexpected value found: %#v", val) + } } From 6a279b9e4cbc020f92aa9cd141dc128b6a6f8a49 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 12 Oct 2023 11:14:47 -0700 Subject: [PATCH 056/105] fieldpath: Implement PathElementMap to map PEs to interface{} --- fieldpath/pathelementmap.go | 43 ++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/fieldpath/pathelementmap.go b/fieldpath/pathelementmap.go index 2611a228..41fc2474 100644 --- a/fieldpath/pathelementmap.go +++ b/fieldpath/pathelementmap.go @@ -28,20 +28,15 @@ import ( // for PathElementSet and SetNodeMap, so we could probably share the // code. type PathElementValueMap struct { - members sortedPathElementValues + valueMap PathElementMap } func MakePathElementValueMap(size int) PathElementValueMap { return PathElementValueMap{ - members: make(sortedPathElementValues, 0, size), + valueMap: MakePathElementMap(size), } } -type pathElementValue struct { - PathElement PathElement - Value value.Value -} - type sortedPathElementValues []pathElementValue // Implement the sort interface; this would permit bulk creation, which would @@ -55,6 +50,38 @@ func (spev sortedPathElementValues) Swap(i, j int) { spev[i], spev[j] = spev[j], // Insert adds the pathelement and associated value in the map. // If insert is called twice with the same PathElement, the value is replaced. func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { + s.valueMap.Insert(pe, v) +} + +// Get retrieves the value associated with the given PathElement from the map. +// (nil, false) is returned if there is no such PathElement. +func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) { + v, ok := s.valueMap.Get(pe) + if !ok { + return nil, false + } + return v.(value.Value), true +} + +// PathElementValueMap is a map from PathElement to interface{}. +type PathElementMap struct { + members sortedPathElementValues +} + +type pathElementValue struct { + PathElement PathElement + Value interface{} +} + +func MakePathElementMap(size int) PathElementMap { + return PathElementMap{ + members: make(sortedPathElementValues, 0, size), + } +} + +// Insert adds the pathelement and associated value in the map. +// If insert is called twice with the same PathElement, the value is replaced. +func (s *PathElementMap) Insert(pe PathElement, v interface{}) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) @@ -73,7 +100,7 @@ func (s *PathElementValueMap) Insert(pe PathElement, v value.Value) { // Get retrieves the value associated with the given PathElement from the map. // (nil, false) is returned if there is no such PathElement. -func (s *PathElementValueMap) Get(pe PathElement) (value.Value, bool) { +func (s *PathElementMap) Get(pe PathElement) (interface{}, bool) { loc := sort.Search(len(s.members), func(i int) bool { return !s.members[i].PathElement.Less(pe) }) From a1f0e958d663e4fb0e3314048dec9be424b53d96 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Mon, 9 Oct 2023 08:41:16 -0700 Subject: [PATCH 057/105] typed: Update compare algorithm to handle duplicates --- typed/compare.go | 109 ++++++++++++++++++++++++++++++++---------- typed/symdiff_test.go | 86 ++++++++++++++++++++++++++++++++- 2 files changed, 167 insertions(+), 28 deletions(-) diff --git a/typed/compare.go b/typed/compare.go index 6fb398ae..ed483cbb 100644 --- a/typed/compare.go +++ b/typed/compare.go @@ -213,7 +213,16 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err lLen = lhs.Length() } - lValues := fieldpath.MakePathElementValueMap(lLen) + maxLength := rLen + if lLen > maxLength { + maxLength = lLen + } + // Contains all the unique PEs between lhs and rhs, exactly once. + // Order doesn't matter since we're just tracking ownership in a set. + allPEs := make([]fieldpath.PathElement, 0, maxLength) + + // Gather all the elements from lhs, indexed by PE, in a list for duplicates. + lValues := fieldpath.MakePathElementMap(lLen) for i := 0; i < lLen; i++ { child := lhs.At(i) pe, err := listItemToPathElement(w.allocator, w.schema, t, child) @@ -224,14 +233,18 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // this element. continue } - // Ignore repeated occurences of `pe`. - if _, found := lValues.Get(pe); found { - continue + + if v, found := lValues.Get(pe); found { + list := v.([]value.Value) + lValues.Insert(pe, append(list, child)) + } else { + lValues.Insert(pe, []value.Value{child}) + allPEs = append(allPEs, pe) } - lValues.Insert(pe, child) } - rValues := fieldpath.MakePathElementValueMap(rLen) + // Gather all the elements from rhs, indexed by PE, in a list for duplicates. + rValues := fieldpath.MakePathElementMap(rLen) for i := 0; i < rLen; i++ { rValue := rhs.At(i) pe, err := listItemToPathElement(w.allocator, w.schema, t, rValue) @@ -242,31 +255,75 @@ func (w *compareWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // this element. continue } - // Ignore repeated occurences of `pe`. - if _, found := rValues.Get(pe); found { - continue + if v, found := rValues.Get(pe); found { + list := v.([]value.Value) + rValues.Insert(pe, append(list, rValue)) + } else { + rValues.Insert(pe, []value.Value{rValue}) + if _, found := lValues.Get(pe); !found { + allPEs = append(allPEs, pe) + } } - rValues.Insert(pe, rValue) - // We can compare with nil if lValue is not present. - lValue, _ := lValues.Get(pe) - errs = append(errs, w.compareListItem(t, pe, lValue, rValue)...) } - // Add items from left that are not in right. - for i := 0; i < lLen; i++ { - lValue := lhs.At(i) - pe, err := listItemToPathElement(w.allocator, w.schema, t, lValue) - if err != nil { - errs = append(errs, errorf("element %v: %v", i, err.Error())...) - // If we can't construct the path element, we can't - // even report errors deeper in the schema, so bail on - // this element. - continue + for _, pe := range allPEs { + lList := []value.Value(nil) + if l, ok := lValues.Get(pe); ok { + lList = l.([]value.Value) } - if _, found := rValues.Get(pe); found { - continue + rList := []value.Value(nil) + if l, ok := rValues.Get(pe); ok { + rList = l.([]value.Value) + } + + switch { + case len(lList) == 0 && len(rList) == 0: + // We shouldn't be here anyway. + return + // Normal use-case: + // We have no duplicates for this PE, compare items one-to-one. + case len(lList) <= 1 && len(rList) <= 1: + lValue := value.Value(nil) + if len(lList) != 0 { + lValue = lList[0] + } + rValue := value.Value(nil) + if len(rList) != 0 { + rValue = rList[0] + } + errs = append(errs, w.compareListItem(t, pe, lValue, rValue)...) + // Duplicates before & after use-case: + // Compare the duplicates lists as if they were atomic, mark modified if they changed. + case len(lList) >= 2 && len(rList) >= 2: + listEqual := func(lList, rList []value.Value) bool { + if len(lList) != len(rList) { + return false + } + for i := range lList { + if !value.Equals(lList[i], rList[i]) { + return false + } + } + return true + } + if !listEqual(lList, rList) { + w.comparison.Modified.Insert(append(w.path, pe)) + } + // Duplicates before & not anymore use-case: + // Rcursively add new non-duplicate items, Remove duplicate marker, + case len(lList) >= 2: + if len(rList) != 0 { + errs = append(errs, w.compareListItem(t, pe, nil, rList[0])...) + } + w.comparison.Removed.Insert(append(w.path, pe)) + // New duplicates use-case: + // Recursively remove old non-duplicate items, add duplicate marker. + case len(rList) >= 2: + if len(lList) != 0 { + errs = append(errs, w.compareListItem(t, pe, lList[0], nil)...) + } + w.comparison.Added.Insert(append(w.path, pe)) } - errs = append(errs, w.compareListItem(t, pe, lValue, nil)...) } return diff --git a/typed/symdiff_test.go b/typed/symdiff_test.go index 57751a73..0ef15a2c 100644 --- a/typed/symdiff_test.go +++ b/typed/symdiff_test.go @@ -596,6 +596,24 @@ var symdiffCases = []symdiffTestCase{{ removed: _NS(), modified: _NS(), added: _NS(_P("setStr", _V("c"))), + }, { + lhs: `{"setStr":["a"]}`, + rhs: `{"setStr":["a","b","b"]}`, + removed: _NS(), + modified: _NS(), + added: _NS(_P("setStr", _V("b"))), + }, { + lhs: `{"setStr":["a","b"]}`, + rhs: `{"setStr":["a","b","b"]}`, + removed: _NS(_P("setStr", _V("b"))), + modified: _NS(), + added: _NS(_P("setStr", _V("b"))), + }, { + lhs: `{"setStr":["b","b"]}`, + rhs: `{"setStr":["a","b","b"]}`, + removed: _NS(), + modified: _NS(), + added: _NS(_P("setStr", _V("a"))), }, { lhs: `{"setStr":["a","b","c"]}`, rhs: `{"setStr":[]}`, @@ -618,6 +636,28 @@ var symdiffCases = []symdiffTestCase{{ removed: _NS(_P("setNumeric", _V(3.14159))), modified: _NS(), added: _NS(_P("setNumeric", _V(3))), + }, { + lhs: `{"setStr":["a","b","b","c","a"]}`, + rhs: `{"setStr":[]}`, + removed: _NS( + _P("setStr", _V("a")), + _P("setStr", _V("b")), + _P("setStr", _V("c")), + ), + modified: _NS(), + added: _NS(), + }, { + lhs: `{"setBool":[true,true]}`, + rhs: `{"setBool":[false]}`, + removed: _NS(_P("setBool", _V(true))), + modified: _NS(), + added: _NS(_P("setBool", _V(false))), + }, { + lhs: `{"setNumeric":[1,2,2,3.14159,1]}`, + rhs: `{"setNumeric":[1,2,3]}`, + removed: _NS(_P("setNumeric", _V(1)), _P("setNumeric", _V(2)), _P("setNumeric", _V(3.14159))), + modified: _NS(), + added: _NS(_P("setNumeric", _V(1)), _P("setNumeric", _V(2)), _P("setNumeric", _V(3))), }}, }, { name: "associative list", @@ -743,6 +783,48 @@ var symdiffCases = []symdiffTestCase{{ removed: _NS(), modified: _NS(_P("atomicList")), added: _NS(), + }, { + lhs: `{"list":[{"key":"a","id":1,"nv":2},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + rhs: `{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`, + removed: _NS( + _P("list", _KBF("key", "b", "id", 1)), + _P("list", _KBF("key", "b", "id", 1), "key"), + _P("list", _KBF("key", "b", "id", 1), "id"), + _P("list", _KBF("key", "a", "id", 1)), + ), + modified: _NS(), + added: _NS( + _P("list", _KBF("key", "a", "id", 1)), + _P("list", _KBF("key", "a", "id", 1), "key"), + _P("list", _KBF("key", "a", "id", 1), "id"), + _P("list", _KBF("key", "a", "id", 2)), + _P("list", _KBF("key", "a", "id", 2), "key"), + _P("list", _KBF("key", "a", "id", 2), "id"), + ), + }, { + lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true,"nv":1}]}`, + removed: _NS(), + modified: _NS(_P("list", _KBF("key", "a", "id", 1))), + added: _NS(), + }, { + lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + removed: _NS(), + modified: _NS(), + added: _NS(), + }, { + lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1,"bv":true},{"key":"a","id":1,"bv":true}]}`, + removed: _NS(), + modified: _NS(), + added: _NS(_P("list", _KBF("key", "b", "id", 1), "bv")), + }, { + lhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}]}`, + rhs: `{"list":[{"key":"a","id":1},{"key":"b","id":1},{"key":"a","id":1,"bv":true}],"atomicList":["unrelated"]}`, + removed: _NS(), + modified: _NS(), + added: _NS(_P("atomicList")), }}, }} @@ -757,11 +839,11 @@ func (tt symdiffTestCase) test(t *testing.T) { t.Parallel() pt := parser.Type(tt.rootTypeName) - tvLHS, err := pt.FromYAML(quint.lhs) + tvLHS, err := pt.FromYAML(quint.lhs, typed.AllowDuplicates) if err != nil { t.Fatalf("failed to parse lhs: %v", err) } - tvRHS, err := pt.FromYAML(quint.rhs) + tvRHS, err := pt.FromYAML(quint.rhs, typed.AllowDuplicates) if err != nil { t.Fatalf("failed to parse rhs: %v", err) } From f8c7b27db408debe9d400306c15189f312682fed Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 19 Oct 2023 15:17:07 -0700 Subject: [PATCH 058/105] Rename lhsOrder and rhsOrder to lhsPEs and rhsPEs --- typed/merge.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index e5d8ec0e..9445b59c 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -180,14 +180,14 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, outLen) - rhsOrder, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) + rhsPEs, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) errs = append(errs, rhsErrs...) - lhsOrder, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) + lhsPEs, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) errs = append(errs, lhsErrs...) sharedOrder := make([]*fieldpath.PathElement, 0, rLen) - for i := range rhsOrder { - pe := &rhsOrder[i] + for i := range rhsPEs { + pe := &rhsPEs[i] if _, ok := observedLHS.Get(*pe); ok { sharedOrder = append(sharedOrder, pe) } @@ -199,11 +199,11 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err sharedOrder = sharedOrder[1:] } - lLen, rLen = len(lhsOrder), len(rhsOrder) + lLen, rLen = len(lhsPEs), len(rhsPEs) for lI, rI := 0, 0; lI < lLen || rI < rLen; { if lI < lLen && rI < rLen { - pe := lhsOrder[lI] - if pe.Equals(rhsOrder[rI]) { + pe := lhsPEs[lI] + if pe.Equals(rhsPEs[rI]) { // merge LHS & RHS items lChild, _ := observedLHS.Get(pe) rChild, _ := observedRHS.Get(pe) @@ -222,14 +222,14 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } continue } - if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsOrder[lI]) { + if _, ok := observedRHS.Get(pe); ok && nextShared != nil && !nextShared.Equals(lhsPEs[lI]) { // shared item, but not the one we want in this round lI++ continue } } if lI < lLen { - pe := lhsOrder[lI] + pe := lhsPEs[lI] if _, ok := observedRHS.Get(pe); !ok { // take LHS item lChild, _ := observedLHS.Get(pe) @@ -244,7 +244,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } if rI < rLen { // Take the RHS item, merge with matching LHS item if possible - pe := rhsOrder[rI] + pe := rhsPEs[rI] lChild, _ := observedLHS.Get(pe) // may be nil rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) From c9d20e7780d588afcf2029dd2f7f8d5ecd55c4b3 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Thu, 12 Oct 2023 19:47:18 -0700 Subject: [PATCH 059/105] typed: Allow duplicates when we merge The goal is to ignore duplicates if they are not included in the partial object, and to replace all of the occurences if they are in the partial object. --- typed/merge.go | 23 +++++++++++----- typed/merge_test.go | 64 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 9445b59c..8179d031 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -180,11 +180,15 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } out := make([]interface{}, 0, outLen) - rhsPEs, observedRHS, rhsErrs := w.indexListPathElements(t, rhs) + rhsPEs, observedRHS, rhsErrs := w.indexListPathElements(t, rhs, false) errs = append(errs, rhsErrs...) - lhsPEs, observedLHS, lhsErrs := w.indexListPathElements(t, lhs) + lhsPEs, observedLHS, lhsErrs := w.indexListPathElements(t, lhs, true) errs = append(errs, lhsErrs...) + if len(errs) != 0 { + return errs + } + sharedOrder := make([]*fieldpath.PathElement, 0, rLen) for i := range rhsPEs { pe := &rhsPEs[i] @@ -199,12 +203,14 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err sharedOrder = sharedOrder[1:] } + mergedRHS := fieldpath.MakePathElementMap(len(rhsPEs)) lLen, rLen = len(lhsPEs), len(rhsPEs) for lI, rI := 0, 0; lI < lLen || rI < rLen; { if lI < lLen && rI < rLen { pe := lhsPEs[lI] if pe.Equals(rhsPEs[rI]) { // merge LHS & RHS items + mergedRHS.Insert(pe, struct{}{}) lChild, _ := observedLHS.Get(pe) rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) @@ -232,7 +238,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err pe := lhsPEs[lI] if _, ok := observedRHS.Get(pe); !ok { // take LHS item - lChild, _ := observedLHS.Get(pe) + lChild := lhs.AtUsing(w.allocator, lI) mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) errs = append(errs, errs...) if mergeOut != nil { @@ -240,11 +246,15 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err } lI++ continue + } else if _, ok := mergedRHS.Get(pe); ok { + // we've already merged it with RHS, we don't want to duplicate it, skip it. + lI++ } } if rI < rLen { // Take the RHS item, merge with matching LHS item if possible pe := rhsPEs[rI] + mergedRHS.Insert(pe, struct{}{}) lChild, _ := observedLHS.Get(pe) // may be nil rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) @@ -272,7 +282,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err return errs } -func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { +func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List, allowDuplicates bool) ([]fieldpath.PathElement, fieldpath.PathElementValueMap, ValidationErrors) { var errs ValidationErrors length := 0 if list != nil { @@ -290,11 +300,12 @@ func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List) ( // this element. continue } - if _, found := observed.Get(pe); found { + if _, found := observed.Get(pe); found && !allowDuplicates { errs = append(errs, errorf("duplicate entries for key %v", pe.String())...) continue + } else if !found { + observed.Insert(pe, child) } - observed.Insert(pe, child) pes = append(pes, pe) } return pes, observed, errs diff --git a/typed/merge_test.go b/typed/merge_test.go index 63745cdb..7c7b552b 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -328,6 +328,34 @@ var mergeCases = []mergeTestCase{{ `{"setStr":["a","b","c","d","e","f","g","h","i","j"]}`, `{"setStr":["1","b","2","h","3","e","4","k","l"]}`, `{"setStr":["a","1","b","c","d","f","g","2","h","i","j","3","e","4","k","l"]}`, + }, { // We have a duplicate in LHS + `{"setStr":["a","b","b"]}`, + `{"setStr":["c"]}`, + `{"setStr":["a","b","b","c"]}`, + }, { // We have a duplicate in LHS. + `{"setStr":["a","b","b"]}`, + `{"setStr":["b"]}`, + `{"setStr":["a","b"]}`, + }, { // We have a duplicate in LHS. + `{"setStr":["a","b","b"]}`, + `{"setStr":["a"]}`, + `{"setStr":["a","b","b"]}`, + }, { // We have a duplicate in LHS. + `{"setStr":["a","b","c","d","e","c"]}`, + `{"setStr":["1","b","2","e","d"]}`, + `{"setStr":["a","1","b","c","2","e","c","d"]}`, + }, { // We have a duplicate in LHS, also present in RHS, keep only one. + `{"setStr":["a","b","c","d","e","c"]}`, + `{"setStr":["1","b","2","c","e","d"]}`, + `{"setStr":["a","1","b","2","c","e","d"]}`, + }, { // We have 2 duplicates in LHS, one is replaced. + `{"setStr":["a","a","b","b"]}`, + `{"setStr":["b","c","d"]}`, + `{"setStr":["a","a","b","c","d"]}`, + }, { // We have 2 duplicates in LHS, and nothing on the right + `{"setStr":["a","a","b","b"]}`, + `{"setStr":[]}`, + `{"setStr":["a","a","b","b"]}`, }, { `{"setBool":[true]}`, `{"setBool":[false]}`, @@ -336,6 +364,22 @@ var mergeCases = []mergeTestCase{{ `{"setNumeric":[1,2,3.14159]}`, `{"setNumeric":[1,2,3]}`, `{"setNumeric":[1,2,3.14159,3]}`, + }, { + `{"setStr":["c","a","g","f","c","a"]}`, + `{"setStr":["c","f","a","g"]}`, + `{"setStr":["c","f","a","g"]}`, + }, { + `{"setNumeric":[1,2,3.14159,1,2]}`, + `{"setNumeric":[1,2,3]}`, + `{"setNumeric":[1,2,3.14159,3]}`, + }, { + `{"setBool":[true,false,true]}`, + `{"setBool":[false]}`, + `{"setBool":[true,false,true]}`, + }, { + `{"setBool":[true,false,true]}`, + `{"setBool":[true]}`, + `{"setBool":[true, false]}`, }, }, }, { @@ -423,6 +467,18 @@ var mergeCases = []mergeTestCase{{ `{"atomicList":["a","a","a"]}`, `{"atomicList":["a","a"]}`, `{"atomicList":["a","a"]}`, + }, { + `{"list":[{"key":"a","id":1,"bv":true},{"key":"b","id":2},{"key":"a","id":1,"bv":false,"nv":2}]}`, + `{"list":[{"key":"a","id":1,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`, + `{"list":[{"key":"a","id":1,"bv":true,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`, + }, { + `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`, + `{"list":[]}`, + `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`, + }, { + `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`, + `{}`, + `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`, }}, }} @@ -437,18 +493,16 @@ func (tt mergeTestCase) test(t *testing.T) { t.Run(fmt.Sprintf("%v-valid-%v", tt.name, i), func(t *testing.T) { t.Parallel() pt := parser.Type(tt.rootTypeName) - - lhs, err := pt.FromYAML(triplet.lhs) + // Former object can have duplicates in sets. + lhs, err := pt.FromYAML(triplet.lhs, typed.AllowDuplicates) if err != nil { t.Fatalf("unable to parser/validate lhs yaml: %v\n%v", err, triplet.lhs) } - rhs, err := pt.FromYAML(triplet.rhs) if err != nil { t.Fatalf("unable to parser/validate rhs yaml: %v\n%v", err, triplet.rhs) } - - out, err := pt.FromYAML(triplet.out) + out, err := pt.FromYAML(triplet.out, typed.AllowDuplicates) if err != nil { t.Fatalf("unable to parser/validate out yaml: %v\n%v", err, triplet.out) } From 4daa91c96db61c7781474a3aad1a9586a7252d85 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 18 Oct 2023 12:52:33 -0700 Subject: [PATCH 060/105] typed: Replace duplicates rather than merging --- typed/merge.go | 9 ++++++--- typed/merge_test.go | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/typed/merge.go b/typed/merge.go index 8179d031..fa227ac4 100644 --- a/typed/merge.go +++ b/typed/merge.go @@ -211,7 +211,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if pe.Equals(rhsPEs[rI]) { // merge LHS & RHS items mergedRHS.Insert(pe, struct{}{}) - lChild, _ := observedLHS.Get(pe) + lChild, _ := observedLHS.Get(pe) // may be nil if the PE is duplicaated. rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) errs = append(errs, errs...) @@ -237,7 +237,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err if lI < lLen { pe := lhsPEs[lI] if _, ok := observedRHS.Get(pe); !ok { - // take LHS item + // take LHS item using At to make sure we get the right item (observed may not contain the right item). lChild := lhs.AtUsing(w.allocator, lI) mergeOut, errs := w.mergeListItem(t, pe, lChild, nil) errs = append(errs, errs...) @@ -255,7 +255,7 @@ func (w *mergingWalker) visitListItems(t *schema.List, lhs, rhs value.List) (err // Take the RHS item, merge with matching LHS item if possible pe := rhsPEs[rI] mergedRHS.Insert(pe, struct{}{}) - lChild, _ := observedLHS.Get(pe) // may be nil + lChild, _ := observedLHS.Get(pe) // may be nil if absent or duplicaated. rChild, _ := observedRHS.Get(pe) mergeOut, errs := w.mergeListItem(t, pe, lChild, rChild) errs = append(errs, errs...) @@ -305,6 +305,9 @@ func (w *mergingWalker) indexListPathElements(t *schema.List, list value.List, a continue } else if !found { observed.Insert(pe, child) + } else { + // Duplicated items are not merged with the new value, make them nil. + observed.Insert(pe, value.NewValueInterface(nil)) } pes = append(pes, pe) } diff --git a/typed/merge_test.go b/typed/merge_test.go index 7c7b552b..1e8562f1 100644 --- a/typed/merge_test.go +++ b/typed/merge_test.go @@ -470,7 +470,7 @@ var mergeCases = []mergeTestCase{{ }, { `{"list":[{"key":"a","id":1,"bv":true},{"key":"b","id":2},{"key":"a","id":1,"bv":false,"nv":2}]}`, `{"list":[{"key":"a","id":1,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`, - `{"list":[{"key":"a","id":1,"bv":true,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`, + `{"list":[{"key":"a","id":1,"nv":3},{"key":"c","id":3},{"key":"b","id":2}]}`, }, { `{"list":[{"key":"a","id":1,"nv":1},{"key":"a","id":1,"nv":2}]}`, `{"list":[]}`, From 35d4e8cac439e20d31e5858386967a5a042d5565 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Tue, 24 Oct 2023 13:55:42 -0700 Subject: [PATCH 061/105] Remove unions logic, keep schema components This removes the generally unused union code from the library in a non-backward incompatible way since the API was not used anyways. The schema isn't changed since we don't want to break existing schema (the unions fields will continue to be just ignored). --- internal/fixture/state.go | 14 -- merge/union_test.go | 234 --------------------------- merge/update.go | 31 ---- typed/typed.go | 57 ------- typed/union.go | 276 -------------------------------- typed/union_test.go | 326 -------------------------------------- 6 files changed, 938 deletions(-) delete mode 100644 merge/union_test.go delete mode 100644 typed/union.go delete mode 100644 typed/union_test.go diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 09463d1a..35fec261 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -535,8 +535,6 @@ type TestCase struct { // Managed, if not nil, is the ManagedFields as expected // after all operations are run. Managed fieldpath.ManagedFields - // Set to true if the test case needs the union behavior enabled. - RequiresUnions bool // ReportInputOnNoop if we don't want to compare the output and // always return it. ReturnInputOnNoop bool @@ -576,7 +574,6 @@ func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) Converter: converter, IgnoredFields: tc.IgnoredFields, ReturnInputOnNoop: tc.ReturnInputOnNoop, - EnableUnions: tc.RequiresUnions, } state := State{ Updater: updaterBuilder.BuildUpdater(), @@ -599,7 +596,6 @@ func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) e Converter: converter, IgnoredFields: tc.IgnoredFields, ReturnInputOnNoop: tc.ReturnInputOnNoop, - EnableUnions: tc.RequiresUnions, } state := State{ Updater: updaterBuilder.BuildUpdater(), @@ -636,15 +632,5 @@ func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) e } } - if !tc.RequiresUnions { - // Re-run the test with unions on. - tc2 := tc - tc2.RequiresUnions = true - err := tc2.TestWithConverter(parser, converter) - if err != nil { - return fmt.Errorf("fails if unions are on: %v", err) - } - } - return nil } diff --git a/merge/union_test.go b/merge/union_test.go deleted file mode 100644 index 9a8ba56b..00000000 --- a/merge/union_test.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -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 merge_test - -import ( - "testing" - - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" - . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" - "sigs.k8s.io/structured-merge-diff/v4/merge" - "sigs.k8s.io/structured-merge-diff/v4/typed" -) - -var unionFieldsParser = func() Parser { - parser, err := typed.NewParser(`types: -- name: unionFields - map: - fields: - - name: numeric - type: - scalar: numeric - - name: string - type: - scalar: string - - name: type - type: - scalar: string - - name: fieldA - type: - scalar: string - - name: fieldB - type: - scalar: string - unions: - - discriminator: type - deduceInvalidDiscriminator: true - fields: - - fieldName: numeric - discriminatorValue: Numeric - - fieldName: string - discriminatorValue: String - - fields: - - fieldName: fieldA - discriminatorValue: FieldA - - fieldName: fieldB - discriminatorValue: FieldB`) - if err != nil { - panic(err) - } - return SameVersionParser{T: parser.Type("unionFields")} -}() - -func TestUnion(t *testing.T) { - tests := map[string]TestCase{ - "union_apply_owns_discriminator": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - numeric: 1 - `, - }, - }, - Object: ` - numeric: 1 - type: Numeric - `, - APIVersion: "v1", - Managed: fieldpath.ManagedFields{ - "default": fieldpath.NewVersionedSet( - _NS( - _P("numeric"), _P("type"), - ), - "v1", - false, - ), - }, - }, - "union_apply_without_discriminator_conflict": { - RequiresUnions: true, - Ops: []Operation{ - Update{ - Manager: "controller", - APIVersion: "v1", - Object: ` - string: "some string" - `, - }, - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - numeric: 1 - `, - Conflicts: merge.Conflicts{ - merge.Conflict{Manager: "controller", Path: _P("type")}, - }, - }, - }, - Object: ` - string: "some string" - type: String - `, - APIVersion: "v1", - Managed: fieldpath.ManagedFields{ - "controller": fieldpath.NewVersionedSet( - _NS( - _P("string"), _P("type"), - ), - "v1", - false, - ), - }, - }, - "union_apply_with_null_value": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - type: Numeric - string: null - numeric: 1 - `, - }, - }, - }, - "union_apply_multiple_unions": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - string: "some string" - fieldA: "fieldA string" - `, - }, - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - numeric: 0 - fieldB: "fieldB string" - `, - }, - }, - Object: ` - type: Numeric - numeric: 0 - fieldB: "fieldB string" - `, - APIVersion: "v1", - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - if err := test.Test(unionFieldsParser); err != nil { - t.Fatal(err) - } - }) - } -} - -func TestUnionErrors(t *testing.T) { - tests := map[string]TestCase{ - "union_apply_two": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - numeric: 1 - string: "some string" - `, - }, - }, - }, - "union_apply_two_and_discriminator": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - type: Numeric - string: "some string" - numeric: 1 - `, - }, - }, - }, - "union_apply_wrong_discriminator": { - RequiresUnions: true, - Ops: []Operation{ - Apply{ - Manager: "default", - APIVersion: "v1", - Object: ` - type: Numeric - string: "some string" - `, - }, - }, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - if test.Test(unionFieldsParser) == nil { - t.Fatal("Should fail") - } - }) - } -} diff --git a/merge/update.go b/merge/update.go index e1540841..7d06eb8a 100644 --- a/merge/update.go +++ b/merge/update.go @@ -34,8 +34,6 @@ type UpdaterBuilder struct { Converter Converter IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set - EnableUnions bool - // Stop comparing the new object with old object after applying. // This was initially used to avoid spurious etcd update, but // since that's vastly inefficient, we've come-up with a better @@ -49,7 +47,6 @@ func (u *UpdaterBuilder) BuildUpdater() *Updater { return &Updater{ Converter: u.Converter, IgnoredFields: u.IgnoredFields, - enableUnions: u.EnableUnions, returnInputOnNoop: u.ReturnInputOnNoop, } } @@ -63,19 +60,9 @@ type Updater struct { // Deprecated: This will eventually become private. IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set - enableUnions bool - returnInputOnNoop bool } -// EnableUnionFeature turns on union handling. It is disabled by default until the -// feature is complete. -// -// Deprecated: Use the builder instead. -func (s *Updater) EnableUnionFeature() { - s.enableUnions = true -} - func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpath.APIVersion, managers fieldpath.ManagedFields, workflow string, force bool) (fieldpath.ManagedFields, *typed.Comparison, error) { conflicts := fieldpath.ManagedFields{} removed := fieldpath.ManagedFields{} @@ -160,12 +147,6 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp if err != nil { return nil, fieldpath.ManagedFields{}, err } - if s.enableUnions { - newObject, err = liveObject.NormalizeUnions(newObject) - if err != nil { - return nil, fieldpath.ManagedFields{}, err - } - } managers, compare, err := s.update(liveObject, newObject, version, managers, manager, true) if err != nil { return nil, fieldpath.ManagedFields{}, err @@ -198,22 +179,10 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel if err != nil { return nil, fieldpath.ManagedFields{}, err } - if s.enableUnions { - configObject, err = configObject.NormalizeUnionsApply(configObject) - if err != nil { - return nil, fieldpath.ManagedFields{}, err - } - } newObject, err := liveObject.Merge(configObject) if err != nil { return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to merge config: %v", err) } - if s.enableUnions { - newObject, err = configObject.NormalizeUnionsApply(newObject) - if err != nil { - return nil, fieldpath.ManagedFields{}, err - } - } lastSet := managers[manager] set, err := configObject.ToFieldSet() if err != nil { diff --git a/typed/typed.go b/typed/typed.go index 035c14bd..9be90282 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -192,63 +192,6 @@ func (tv TypedValue) ExtractItems(items *fieldpath.Set) *TypedValue { return &tv } -// NormalizeUnions takes the new object and normalizes the union: -// - If discriminator changed to non-nil, and a new field has been added -// that doesn't match, an error is returned, -// - If discriminator hasn't changed and two fields or more are set, an -// error is returned, -// - If discriminator changed to non-nil, all other fields but the -// discriminated one will be cleared, -// - Otherwise, If only one field is left, update discriminator to that value. -// -// Please note: union behavior isn't finalized yet and this is still experimental. -func (tv TypedValue) NormalizeUnions(new *TypedValue) (*TypedValue, error) { - var errs ValidationErrors - var normalizeFn = func(w *mergingWalker) { - if w.rhs != nil { - v := w.rhs.Unstructured() - w.out = &v - } - if err := normalizeUnions(w); err != nil { - errs = append(errs, errorf(err.Error())...) - } - } - out, mergeErrs := merge(&tv, new, func(w *mergingWalker) {}, normalizeFn) - if mergeErrs != nil { - errs = append(errs, mergeErrs.(ValidationErrors)...) - } - if len(errs) > 0 { - return nil, errs - } - return out, nil -} - -// NormalizeUnionsApply specifically normalize unions on apply. It -// validates that the applied union is correct (there should be no -// ambiguity there), and clear the fields according to the sent intent. -// -// Please note: union behavior isn't finalized yet and this is still experimental. -func (tv TypedValue) NormalizeUnionsApply(new *TypedValue) (*TypedValue, error) { - var errs ValidationErrors - var normalizeFn = func(w *mergingWalker) { - if w.rhs != nil { - v := w.rhs.Unstructured() - w.out = &v - } - if err := normalizeUnionsApply(w); err != nil { - errs = append(errs, errorf(err.Error())...) - } - } - out, mergeErrs := merge(&tv, new, func(w *mergingWalker) {}, normalizeFn) - if mergeErrs != nil { - errs = append(errs, mergeErrs.(ValidationErrors)...) - } - if len(errs) > 0 { - return nil, errs - } - return out, nil -} - func (tv TypedValue) Empty() *TypedValue { tv.value = value.NewValueInterface(nil) return &tv diff --git a/typed/union.go b/typed/union.go deleted file mode 100644 index 1fa5d88a..00000000 --- a/typed/union.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -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 typed - -import ( - "fmt" - "strings" - - "sigs.k8s.io/structured-merge-diff/v4/schema" - "sigs.k8s.io/structured-merge-diff/v4/value" -) - -func normalizeUnions(w *mergingWalker) error { - atom, found := w.schema.Resolve(w.typeRef) - if !found { - panic(fmt.Sprintf("Unable to resolve schema in normalize union: %v/%v", w.schema, w.typeRef)) - } - // Unions can only be in structures, and the struct must not have been removed - if atom.Map == nil || w.out == nil { - return nil - } - - var old value.Map - if w.lhs != nil && !w.lhs.IsNull() { - old = w.lhs.AsMap() - } - for _, union := range atom.Map.Unions { - if err := newUnion(&union).Normalize(old, w.rhs.AsMap(), value.NewValueInterface(*w.out).AsMap()); err != nil { - return err - } - } - return nil -} - -func normalizeUnionsApply(w *mergingWalker) error { - atom, found := w.schema.Resolve(w.typeRef) - if !found { - panic(fmt.Sprintf("Unable to resolve schema in normalize union: %v/%v", w.schema, w.typeRef)) - } - // Unions can only be in structures, and the struct must not have been removed - if atom.Map == nil || w.out == nil { - return nil - } - - var old value.Map - if w.lhs != nil && !w.lhs.IsNull() { - old = w.lhs.AsMap() - } - - for _, union := range atom.Map.Unions { - out := value.NewValueInterface(*w.out) - if err := newUnion(&union).NormalizeApply(old, w.rhs.AsMap(), out.AsMap()); err != nil { - return err - } - *w.out = out.Unstructured() - } - return nil -} - -type discriminated string -type field string - -type discriminatedNames struct { - f2d map[field]discriminated - d2f map[discriminated]field -} - -func newDiscriminatedName(f2d map[field]discriminated) discriminatedNames { - d2f := map[discriminated]field{} - for key, value := range f2d { - d2f[value] = key - } - return discriminatedNames{ - f2d: f2d, - d2f: d2f, - } -} - -func (dn discriminatedNames) toField(d discriminated) field { - if f, ok := dn.d2f[d]; ok { - return f - } - return field(d) -} - -func (dn discriminatedNames) toDiscriminated(f field) discriminated { - if d, ok := dn.f2d[f]; ok { - return d - } - return discriminated(f) -} - -type discriminator struct { - name string -} - -func (d *discriminator) Set(m value.Map, v discriminated) { - if d == nil { - return - } - m.Set(d.name, value.NewValueInterface(string(v))) -} - -func (d *discriminator) Get(m value.Map) discriminated { - if d == nil || m == nil { - return "" - } - val, ok := m.Get(d.name) - if !ok { - return "" - } - if !val.IsString() { - return "" - } - return discriminated(val.AsString()) -} - -type fieldsSet map[field]struct{} - -// newFieldsSet returns a map of the fields that are part of the union and are set -// in the given map. -func newFieldsSet(m value.Map, fields []field) fieldsSet { - if m == nil { - return nil - } - set := fieldsSet{} - for _, f := range fields { - if subField, ok := m.Get(string(f)); ok && !subField.IsNull() { - set.Add(f) - } - } - return set -} - -func (fs fieldsSet) Add(f field) { - if fs == nil { - fs = map[field]struct{}{} - } - fs[f] = struct{}{} -} - -func (fs fieldsSet) One() *field { - for f := range fs { - return &f - } - return nil -} - -func (fs fieldsSet) Has(f field) bool { - _, ok := fs[f] - return ok -} - -func (fs fieldsSet) List() []field { - fields := []field{} - for f := range fs { - fields = append(fields, f) - } - return fields -} - -func (fs fieldsSet) Difference(o fieldsSet) fieldsSet { - n := fieldsSet{} - for f := range fs { - if !o.Has(f) { - n.Add(f) - } - } - return n -} - -func (fs fieldsSet) String() string { - s := []string{} - for k := range fs { - s = append(s, string(k)) - } - return strings.Join(s, ", ") -} - -type union struct { - deduceInvalidDiscriminator bool - d *discriminator - dn discriminatedNames - f []field -} - -func newUnion(su *schema.Union) *union { - u := &union{} - if su.Discriminator != nil { - u.d = &discriminator{name: *su.Discriminator} - } - f2d := map[field]discriminated{} - for _, f := range su.Fields { - u.f = append(u.f, field(f.FieldName)) - f2d[field(f.FieldName)] = discriminated(f.DiscriminatorValue) - } - u.dn = newDiscriminatedName(f2d) - u.deduceInvalidDiscriminator = su.DeduceInvalidDiscriminator - return u -} - -// clear removes all the fields in map that are part of the union, but -// the one we decided to keep. -func (u *union) clear(m value.Map, f field) { - for _, fieldName := range u.f { - if field(fieldName) != f { - m.Delete(string(fieldName)) - } - } -} - -func (u *union) Normalize(old, new, out value.Map) error { - os := newFieldsSet(old, u.f) - ns := newFieldsSet(new, u.f) - diff := ns.Difference(os) - - if u.d.Get(old) != u.d.Get(new) && u.d.Get(new) != "" { - if len(diff) == 1 && u.d.Get(new) != u.dn.toDiscriminated(*diff.One()) { - return fmt.Errorf("discriminator (%v) and field changed (%v) don't match", u.d.Get(new), diff.One()) - } - if len(diff) > 1 { - return fmt.Errorf("multiple new fields added: %v", diff) - } - u.clear(out, u.dn.toField(u.d.Get(new))) - return nil - } - - if len(ns) > 1 { - return fmt.Errorf("multiple fields set without discriminator change: %v", ns) - } - - // Set discriminiator if it needs to be deduced. - if u.deduceInvalidDiscriminator && len(ns) == 1 { - u.d.Set(out, u.dn.toDiscriminated(*ns.One())) - } - - return nil -} - -func (u *union) NormalizeApply(applied, merged, out value.Map) error { - as := newFieldsSet(applied, u.f) - if len(as) > 1 { - return fmt.Errorf("more than one field of union applied: %v", as) - } - if len(as) == 0 { - // None is set, just leave. - return nil - } - // We have exactly one, discriminiator must match if set - if u.d.Get(applied) != "" && u.d.Get(applied) != u.dn.toDiscriminated(*as.One()) { - return fmt.Errorf("applied discriminator (%v) doesn't match applied field (%v)", u.d.Get(applied), *as.One()) - } - - // Update discriminiator if needed - if u.deduceInvalidDiscriminator { - u.d.Set(out, u.dn.toDiscriminated(*as.One())) - } - // Clear others fields. - u.clear(out, *as.One()) - - return nil -} diff --git a/typed/union_test.go b/typed/union_test.go deleted file mode 100644 index a3ed129a..00000000 --- a/typed/union_test.go +++ /dev/null @@ -1,326 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -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 typed_test - -import ( - "testing" - - "sigs.k8s.io/structured-merge-diff/v4/typed" -) - -var unionParser = func() typed.ParseableType { - parser, err := typed.NewParser(`types: -- name: union - map: - fields: - - name: discriminator - type: - scalar: string - - name: one - type: - scalar: numeric - - name: two - type: - scalar: numeric - - name: three - type: - scalar: numeric - - name: letter - type: - scalar: string - - name: a - type: - scalar: numeric - - name: b - type: - scalar: numeric - unions: - - discriminator: discriminator - deduceInvalidDiscriminator: true - fields: - - fieldName: one - discriminatorValue: One - - fieldName: two - discriminatorValue: TWO - - fieldName: three - discriminatorValue: three - - discriminator: letter - fields: - - fieldName: a - discriminatorValue: A - - fieldName: b - discriminatorValue: b`) - if err != nil { - panic(err) - } - return parser.Type("union") -}() - -func TestNormalizeUnions(t *testing.T) { - tests := []struct { - name string - old typed.YAMLObject - new typed.YAMLObject - out typed.YAMLObject - }{ - { - name: "nothing changed, add discriminator", - old: `{"one": 1}`, - new: `{"one": 1}`, - out: `{"one": 1, "discriminator": "One"}`, - }, - { - name: "nothing changed, non-deduced", - old: `{"a": 1}`, - new: `{"a": 1}`, - out: `{"a": 1}`, - }, - { - name: "proper union update, setting discriminator", - old: `{"one": 1}`, - new: `{"two": 1}`, - out: `{"two": 1, "discriminator": "TWO"}`, - }, - { - name: "proper union update, non-deduced", - old: `{"a": 1}`, - new: `{"b": 1}`, - out: `{"b": 1}`, - }, - { - name: "proper union update from not-set, setting discriminator", - old: `{}`, - new: `{"two": 1}`, - out: `{"two": 1, "discriminator": "TWO"}`, - }, - { - name: "proper union update from not-set, non-deduced", - old: `{}`, - new: `{"b": 1}`, - out: `{"b": 1}`, - }, - { - name: "remove union, with discriminator", - old: `{"one": 1}`, - new: `{}`, - out: `{}`, - }, - { - name: "remove union and discriminator", - old: `{"one": 1, "discriminator": "One"}`, - new: `{}`, - out: `{}`, - }, - { - name: "remove union, not discriminator", - old: `{"one": 1, "discriminator": "One"}`, - new: `{"discriminator": "One"}`, - out: `{"discriminator": "One"}`, - }, - { - name: "remove union, not discriminator, non-deduced", - old: `{"a": 1, "letter": "A"}`, - new: `{"letter": "A"}`, - out: `{"letter": "A"}`, - }, - { - name: "change discriminator, nothing else", - old: `{"discriminator": "One"}`, - new: `{"discriminator": "random"}`, - out: `{"discriminator": "random"}`, - }, - { - name: "change discriminator, nothing else, non-deduced", - old: `{"letter": "A"}`, - new: `{"letter": "b"}`, - out: `{"letter": "b"}`, - }, - { - name: "change discriminator, nothing else, it drops other field", - old: `{"discriminator": "One", "one": 1}`, - new: `{"discriminator": "random", "one": 1}`, - out: `{"discriminator": "random"}`, - }, - { - name: "change discriminator, nothing else, it drops other field, non-deduced", - old: `{"letter": "A", "a": 1}`, - new: `{"letter": "b", "a": 1}`, - out: `{"letter": "b"}`, - }, - { - name: "remove discriminator, nothing else", - old: `{"discriminator": "One", "one": 1}`, - new: `{"one": 1}`, - out: `{"one": 1, "discriminator": "One"}`, - }, - { - name: "remove discriminator, nothing else, non-deduced", - old: `{"letter": "A", "a": 1}`, - new: `{"a": 1}`, - out: `{"a": 1}`, - }, - { - name: "remove discriminator, add new field", - old: `{"discriminator": "One", "one": 1}`, - new: `{"two": 1}`, - out: `{"two": 1, "discriminator": "TWO"}`, - }, - { - name: "remove discriminator, add new field, non-deduced", - old: `{"letter": "A", "a": 1}`, - new: `{"b": 1}`, - out: `{"b": 1}`, - }, - { - name: "both fields removed", - old: `{"one": 1, "two": 1}`, - new: `{}`, - out: `{}`, - }, - { - name: "one field removed", - old: `{"one": 1, "two": 1}`, - new: `{"one": 1}`, - out: `{"one": 1, "discriminator": "One"}`, - }, - { - name: "one field removed, non-deduced", - old: `{"a": 1, "b": 1}`, - new: `{"a": 1}`, - out: `{"a": 1}`, - }, - // These use-cases shouldn't happen: - { - name: "one field removed, discriminator unchanged", - old: `{"one": 1, "two": 1, "discriminator": "TWO"}`, - new: `{"one": 1, "discriminator": "TWO"}`, - out: `{"one": 1, "discriminator": "One"}`, - }, - { - name: "one field removed, discriminator unchanged, non-deduced", - old: `{"a": 1, "b": 1, "letter": "b"}`, - new: `{"a": 1, "letter": "b"}`, - out: `{"a": 1, "letter": "b"}`, - }, - { - name: "one field removed, discriminator added", - old: `{"two": 2, "one": 1}`, - new: `{"one": 1, "discriminator": "TWO"}`, - out: `{"discriminator": "TWO"}`, - }, - { - name: "one field removed, discriminator added, non-deduced", - old: `{"b": 2, "a": 1}`, - new: `{"a": 1, "letter": "b"}`, - out: `{"letter": "b"}`, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - old, err := unionParser.FromYAML(test.old) - if err != nil { - t.Fatalf("Failed to parse old object: %v", err) - } - new, err := unionParser.FromYAML(test.new) - if err != nil { - t.Fatalf("failed to parse new object: %v", err) - } - out, err := unionParser.FromYAML(test.out) - if err != nil { - t.Fatalf("failed to parse out object: %v", err) - } - got, err := old.NormalizeUnions(new) - if err != nil { - t.Fatalf("failed to normalize unions: %v", err) - } - comparison, err := out.Compare(got) - if err != nil { - t.Fatalf("failed to compare result and expected: %v", err) - } - if !comparison.IsSame() { - t.Errorf("Result is different from expected:\n%v", comparison) - } - }) - } -} - -func TestNormalizeUnionError(t *testing.T) { - tests := []struct { - name string - old typed.YAMLObject - new typed.YAMLObject - }{ - { - name: "dumb client update, no discriminator", - old: `{"one": 1}`, - new: `{"one": 2, "two": 1}`, - }, - { - name: "new object has three of same union set", - old: `{"one": 1}`, - new: `{"one": 2, "two": 1, "three": 3}`, - }, - { - name: "dumb client doesn't update discriminator", - old: `{"one": 1, "discriminator": "One"}`, - new: `{"one": 2, "two": 1, "discriminator": "One"}`, - }, - { - name: "client sends new field that and discriminator change", - old: `{}`, - new: `{"one": 1, "discriminator": "Two"}`, - }, - { - name: "client sends new fields that don't match discriminator change", - old: `{}`, - new: `{"one": 1, "two": 1, "discriminator": "One"}`, - }, - { - name: "old object has two of same union set", - old: `{"one": 1, "two": 2}`, - new: `{"one": 2, "two": 1}`, - }, - { - name: "old object has two of same union, but we add third", - old: `{"discriminator": "One", "one": 1, "two": 1}`, - new: `{"discriminator": "One", "one": 1, "two": 1, "three": 1}`, - }, - { - name: "one field removed, 2 left, discriminator unchanged", - old: `{"one": 1, "two": 1, "three": 1, "discriminator": "TWO"}`, - new: `{"one": 1, "two": 1, "discriminator": "TWO"}`, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - old, err := unionParser.FromYAML(test.old) - if err != nil { - t.Fatalf("Failed to parse old object: %v", err) - } - new, err := unionParser.FromYAML(test.new) - if err != nil { - t.Fatalf("failed to parse new object: %v", err) - } - _, err = old.NormalizeUnions(new) - if err == nil { - t.Fatal("Normalization should have failed, but hasn't.") - } - }) - } -} From 29babbca77354d7e676c69c2eca23f55bf5bf3d3 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Fri, 16 Jun 2023 09:26:33 -0700 Subject: [PATCH 062/105] merge: Allow duplicate keys in lhs This not only affects merge but also `Compare` since they use the same algorithm/code. Duplicates fields in a set/associative-list will now be treated as an atomic entity within that list, and will be entirely owned by the person who made them duplicates or who changed one of the duplicates. --- internal/fixture/state.go | 6 +- merge/duplicates_test.go | 808 ++++++++++++++++++++++++++++++++++++++ merge/update.go | 2 +- 3 files changed, 812 insertions(+), 4 deletions(-) create mode 100644 merge/duplicates_test.go diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 35fec261..5b5d4829 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -131,7 +131,7 @@ func (s *State) UpdateObject(tv *typed.TypedValue, version fieldpath.APIVersion, // Update the current state with the passed in object func (s *State) Update(obj typed.YAMLObject, version fieldpath.APIVersion, manager string) error { - tv, err := s.Parser.Type(string(version)).FromYAML(FixTabsOrDie(obj)) + tv, err := s.Parser.Type(string(version)).FromYAML(FixTabsOrDie(obj), typed.AllowDuplicates) if err != nil { return err } @@ -174,7 +174,7 @@ func (s *State) CompareLive(obj typed.YAMLObject, version fieldpath.APIVersion) if err := s.checkInit(version); err != nil { return "", err } - tv, err := s.Parser.Type(string(version)).FromYAML(obj) + tv, err := s.Parser.Type(string(version)).FromYAML(obj, typed.AllowDuplicates) if err != nil { return "", err } @@ -461,7 +461,7 @@ func (u Update) run(state *State) error { } func (u Update) preprocess(parser Parser) (Operation, error) { - tv, err := parser.Type(string(u.APIVersion)).FromYAML(FixTabsOrDie(u.Object)) + tv, err := parser.Type(string(u.APIVersion)).FromYAML(FixTabsOrDie(u.Object), typed.AllowDuplicates) if err != nil { return nil, err } diff --git a/merge/duplicates_test.go b/merge/duplicates_test.go new file mode 100644 index 00000000..e0724370 --- /dev/null +++ b/merge/duplicates_test.go @@ -0,0 +1,808 @@ +/* +Copyright 2019 The Kubernetes Authors. + +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 merge_test + +import ( + "testing" + + "sigs.k8s.io/structured-merge-diff/v4/fieldpath" + . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" + "sigs.k8s.io/structured-merge-diff/v4/merge" + "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +var duplicatesParser = func() Parser { + parser, err := typed.NewParser(`types: +- name: type + map: + fields: + - name: list + type: + namedType: associativeList + - name: unrelated + type: + scalar: numeric + - name: set + type: + namedType: set +- name: associativeList + list: + elementType: + namedType: myElement + elementRelationship: associative + keys: + - name +- name: myElement + map: + fields: + - name: name + type: + scalar: string + - name: value1 + type: + scalar: numeric + - name: value2 + type: + scalar: numeric +- name: set + list: + elementType: + scalar: numeric + elementRelationship: associative +`) + if err != nil { + panic(err) + } + return SameVersionParser{T: parser.Type("type")} +}() + +func TestDuplicates(t *testing.T) { + tests := map[string]TestCase{ + "sets/ownership/duplicates": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + set: [1, 1, 3, 4] + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("set"), + _P("set", _V(1)), + _P("set", _V(3)), + _P("set", _V(4)), + ), + "v1", + false, + ), + }, + }, + "sets/ownership/add_duplicate": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + set: [1, 3, 4] + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + set: [1, 1, 3, 4] + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("set"), + _P("set", _V(3)), + _P("set", _V(4)), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("set", _V(1)), + ), + "v1", + false, + ), + }, + }, + "sets/ownership/remove_duplicate": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + set: [1, 1, 3, 4] + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + set: [1, 3, 4] + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("set"), + _P("set", _V(3)), + _P("set", _V(4)), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("set", _V(1)), + ), + "v1", + false, + ), + }, + }, + "sets/merging/remove_duplicate": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + set: [1, 1, 3, 4] + `, + APIVersion: "v1", + }, + Apply{ + Manager: "applier", + Object: ` + set: [1] + `, + APIVersion: "v1", + Conflicts: merge.Conflicts{ + {Manager: "updater", Path: _P("set", _V(1))}, + }, + }, + ForceApply{ + Manager: "applier", + Object: ` + set: [1] + `, + APIVersion: "v1", + }, + }, + Object: ` + set: [1, 3, 4] + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("set"), + _P("set", _V(3)), + _P("set", _V(4)), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("set", _V(1)), + ), + "v1", + true, + ), + }, + }, + "sets/merging/ignore_duplicate": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + set: [1, 1, 3, 4] + `, + APIVersion: "v1", + }, + Apply{ + Manager: "applier", + Object: ` + set: [5] + `, + APIVersion: "v1", + }, + }, + Object: ` + set: [1, 1, 3, 4, 5] + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("set"), + _P("set", _V(1)), + _P("set", _V(3)), + _P("set", _V(4)), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("set", _V(5)), + ), + "v1", + true, + ), + }, + }, + "list/ownership/duplicated_items": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + // `name: a` is only owned once. + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "a")), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + }, + }, + "list/ownership/change_duplicated_items": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 3 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + // `name: a` is only owned once, by actor who changed some of it. + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + ), + "v1", + false, + ), + }, + }, + "list/ownership/change_fields_duplicated_items": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + value2: 3 # New field + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + ), + "v1", + false, + ), + }, + }, + "list/ownership/add_duplicated_items_different_field": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + list: + - name: a + value1: 1 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + list: + - name: a + value1: 1 + - name: a + value2: 3 # New field + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + ), + "v1", + false, + ), + }, + }, + "list/ownership/add_unrelated_to_list_with_duplicates": { + Ops: []Operation{ + Update{ + Manager: "updater-one", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater-two", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + Managed: fieldpath.ManagedFields{ + "updater-one": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "a")), + ), + "v1", + false, + ), + "updater-two": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + }, + }, + "list/merge/unrelated_with_duplicated_items": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + ForceApply{ + Manager: "applier", + Object: ` + unrelated: 5 + `, + APIVersion: "v1", + }, + }, + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + unrelated: 5 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "a")), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("unrelated"), + ), + "v1", + true, + ), + }, + }, + // TODO: Owning the key is a little messed-up. + "list/merge/change_duplicated_item": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Apply{ + Manager: "applier", + Object: ` + list: + - name: a + value1: 3 + `, + APIVersion: "v1", + Conflicts: merge.Conflicts{ + {Manager: "updater", Path: _P("list", _KBF("name", "a"))}, + }, + }, + ForceApply{ + Manager: "applier", + Object: ` + list: + - name: a + value1: 3 + `, + APIVersion: "v1", + }, + }, + Object: ` + list: + - name: a + value1: 3 + - name: b + value1: 3 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + _P("list", _KBF("name", "a"), "name"), + _P("list", _KBF("name", "a"), "value1"), + ), + "v1", + true, + ), + }, + }, + + "list/merge/unchanged_duplicated_item": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Apply{ + Manager: "applier", + Object: ` + list: + - name: a + value1: 2 + `, + APIVersion: "v1", + Conflicts: merge.Conflicts{ + {Manager: "updater", Path: _P("list", _KBF("name", "a"))}, + }, + }, + ForceApply{ + Manager: "applier", + Object: ` + list: + - name: a + value1: 3 + `, + APIVersion: "v1", + }, + }, + Object: ` + list: + - name: a + value1: 3 + - name: b + value1: 3 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + _P("list", _KBF("name", "a"), "name"), + _P("list", _KBF("name", "a"), "value1"), + ), + "v1", + true, + ), + }, + }, + "list/merge/change_non_duplicated_item": { + Ops: []Operation{ + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + ForceApply{ + Manager: "applier", + Object: ` + list: + - name: b + value1: 4 + `, + APIVersion: "v1", + }, + }, + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 4 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list"), + _P("list", _KBF("name", "a")), + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + ), + "v1", + false, + ), + "applier": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + true, + ), + }, + }, + "list/merge/apply_update_duplicates_apply_without": { + Ops: []Operation{ + Apply{ + Manager: "applier", + Object: ` + list: + - name: a + value1: 1 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Update{ + Manager: "updater", + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + Apply{ + Manager: "applier", + Object: ` + list: + - name: b + value1: 3 + `, + APIVersion: "v1", + }, + }, + Object: ` + list: + - name: a + value1: 1 + - name: a + value1: 2 + - name: b + value1: 3 + `, + APIVersion: "v1", + Managed: fieldpath.ManagedFields{ + "applier": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "b")), + _P("list", _KBF("name", "b"), "name"), + _P("list", _KBF("name", "b"), "value1"), + ), + "v1", + true, + ), + "updater": fieldpath.NewVersionedSet( + _NS( + _P("list", _KBF("name", "a")), + ), + "v1", + false, + ), + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if err := test.Test(duplicatesParser); err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/merge/update.go b/merge/update.go index 7d06eb8a..d5a977d6 100644 --- a/merge/update.go +++ b/merge/update.go @@ -160,7 +160,7 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp ignored = fieldpath.NewSet() } managers[manager] = fieldpath.NewVersionedSet( - managers[manager].Set().Union(compare.Modified).Union(compare.Added).Difference(compare.Removed).RecursiveDifference(ignored), + managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added).RecursiveDifference(ignored), version, false, ) From f0cbe01eda704c135ee99975eb2378b2bd8d008f Mon Sep 17 00:00:00 2001 From: Jordan Rodgers Date: Tue, 14 May 2024 23:42:29 -0700 Subject: [PATCH 063/105] fix panic when calling ToUnstructured on nil metav1.Time --- value/reflectcache.go | 10 ++++---- value/reflectcache_test.go | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/value/reflectcache.go b/value/reflectcache.go index f0d58d42..55123c38 100644 --- a/value/reflectcache.go +++ b/value/reflectcache.go @@ -184,6 +184,11 @@ func (e TypeReflectCacheEntry) ToUnstructured(sv reflect.Value) (interface{}, er // This is based on https://github.com/kubernetes/kubernetes/blob/82c9e5c814eb7acc6cc0a090c057294d0667ad66/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L505 // and is intended to replace it. + // Check if the object is a nil pointer. + if sv.Kind() == reflect.Ptr && sv.IsNil() { + // We're done - we don't need to store anything. + return nil, nil + } // Check if the object has a custom string converter and use it if available, since it is much more efficient // than round tripping through json. if converter, ok := e.getUnstructuredConverter(sv); ok { @@ -191,11 +196,6 @@ func (e TypeReflectCacheEntry) ToUnstructured(sv reflect.Value) (interface{}, er } // Check if the object has a custom JSON marshaller/unmarshaller. if marshaler, ok := e.getJsonMarshaler(sv); ok { - if sv.Kind() == reflect.Ptr && sv.IsNil() { - // We're done - we don't need to store anything. - return nil, nil - } - data, err := marshaler.MarshalJSON() if err != nil { return nil, err diff --git a/value/reflectcache_test.go b/value/reflectcache_test.go index 523e44a0..c86eeb7a 100644 --- a/value/reflectcache_test.go +++ b/value/reflectcache_test.go @@ -19,6 +19,7 @@ package value import ( "reflect" "testing" + "time" ) type CustomValue struct { @@ -39,6 +40,21 @@ func (c *CustomPointer) MarshalJSON() ([]byte, error) { return c.data, nil } +// Mimics https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/time.go. +type Time struct { + time.Time +} + +// ToUnstructured implements the value.UnstructuredConverter interface. +func (t Time) ToUnstructured() interface{} { + if t.IsZero() { + return nil + } + buf := make([]byte, 0, len(time.RFC3339)) + buf = t.UTC().AppendFormat(buf, time.RFC3339) + return string(buf) +} + func TestToUnstructured(t *testing.T) { testcases := []struct { Data string @@ -78,6 +94,39 @@ func TestToUnstructured(t *testing.T) { } } +func timePtr(t time.Time) *time.Time { return &t } + +func TestTimeToUnstructured(t *testing.T) { + testcases := []struct { + Name string + Time *time.Time + Expected interface{} + }{ + {Name: "nil", Time: nil, Expected: nil}, + {Name: "zero", Time: &time.Time{}, Expected: nil}, + {Name: "1", Time: timePtr(time.Time{}.Add(time.Second)), Expected: "0001-01-01T00:00:01Z"}, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + t.Parallel() + var time *Time + rv := reflect.ValueOf(time) + if tc.Time != nil { + rv = reflect.ValueOf(Time{Time: *tc.Time}) + } + result, err := TypeReflectEntryOf(rv.Type()).ToUnstructured(rv) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(result, tc.Expected) { + t.Errorf("expected %#v but got %#v", tc.Expected, result) + } + }) + } +} + func TestTypeReflectEntryOf(t *testing.T) { testString := "" tests := map[string]struct { From ee5baba31ca53e3631e0e9813e89950809730938 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 5 Jun 2024 19:46:47 +0000 Subject: [PATCH 064/105] Add jpbetz to SMD approvers Per SIG TL responsibilities --- OWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OWNERS b/OWNERS index 23b0aaa2..71704c73 100644 --- a/OWNERS +++ b/OWNERS @@ -2,9 +2,10 @@ approvers: - apelisse + - jpbetz emeritus_approvers: - lavalamp # 2023-05-13 - jennybuckley # 2021-05-13 reviewers: - apelisse - - jpbetz + From f07895d137f456f3a6fee7a5a0ae6e1ba60ec95c Mon Sep 17 00:00:00 2001 From: Ben Luddy Date: Wed, 5 Jun 2024 13:18:06 -0400 Subject: [PATCH 065/105] Error on conversion to unstructured for invalid json.Marshalers. If a type's implementation of json.Marshaler returns bytes representing a valid JSON object or array followed by anything other than trailing whitespace, return an error rather than ignoring the trailing data. The documentation for the Marshaler interface indicates that implementations shouldn't do this, but it is safer to check (as json.Marshal does) than to rely on it. --- value/reflectcache.go | 53 +++++++----- value/reflectcache_test.go | 160 ++++++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 22 deletions(-) diff --git a/value/reflectcache.go b/value/reflectcache.go index 55123c38..88693b87 100644 --- a/value/reflectcache.go +++ b/value/reflectcache.go @@ -19,7 +19,9 @@ package value import ( "bytes" "encoding/json" + "errors" "fmt" + "io" "reflect" "sort" "sync" @@ -379,34 +381,47 @@ const maxDepth = 10000 // unmarshal unmarshals the given data // If v is a *map[string]interface{}, numbers are converted to int64 or float64 func unmarshal(data []byte, v interface{}) error { + // Build a decoder from the given data + decoder := json.NewDecoder(bytes.NewBuffer(data)) + // Preserve numbers, rather than casting to float64 automatically + decoder.UseNumber() + // Run the decode + if err := decoder.Decode(v); err != nil { + return err + } + next := decoder.InputOffset() + if _, err := decoder.Token(); !errors.Is(err, io.EOF) { + tail := bytes.TrimLeft(data[next:], " \t\r\n") + return fmt.Errorf("unexpected trailing data at offset %d", len(data)-len(tail)) + } + + // If the decode succeeds, post-process the object to convert json.Number objects to int64 or float64 switch v := v.(type) { case *map[string]interface{}: - // Build a decoder from the given data - decoder := json.NewDecoder(bytes.NewBuffer(data)) - // Preserve numbers, rather than casting to float64 automatically - decoder.UseNumber() - // Run the decode - if err := decoder.Decode(v); err != nil { - return err - } - // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64 return convertMapNumbers(*v, 0) case *[]interface{}: - // Build a decoder from the given data - decoder := json.NewDecoder(bytes.NewBuffer(data)) - // Preserve numbers, rather than casting to float64 automatically - decoder.UseNumber() - // Run the decode - if err := decoder.Decode(v); err != nil { - return err - } - // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64 return convertSliceNumbers(*v, 0) + case *interface{}: + return convertInterfaceNumbers(v, 0) + default: - return json.Unmarshal(data, v) + return nil + } +} + +func convertInterfaceNumbers(v *interface{}, depth int) error { + var err error + switch v2 := (*v).(type) { + case json.Number: + *v, err = convertNumber(v2) + case map[string]interface{}: + err = convertMapNumbers(v2, depth+1) + case []interface{}: + err = convertSliceNumbers(v2, depth+1) } + return err } // convertMapNumbers traverses the map, converting any json.Number values to int64 or float64. diff --git a/value/reflectcache_test.go b/value/reflectcache_test.go index c86eeb7a..33eae60c 100644 --- a/value/reflectcache_test.go +++ b/value/reflectcache_test.go @@ -17,6 +17,8 @@ limitations under the License. package value import ( + "encoding/json" + "fmt" "reflect" "testing" "time" @@ -57,8 +59,9 @@ func (t Time) ToUnstructured() interface{} { func TestToUnstructured(t *testing.T) { testcases := []struct { - Data string - Expected interface{} + Data string + Expected interface{} + ExpectedErrorMessage string }{ {Data: `null`, Expected: nil}, {Data: `true`, Expected: true}, @@ -69,6 +72,12 @@ func TestToUnstructured(t *testing.T) { {Data: `{"a":1}`, Expected: map[string]interface{}{"a": int64(1)}}, {Data: `0`, Expected: int64(0)}, {Data: `0.0`, Expected: float64(0)}, + {Data: "{} \t\r\n", Expected: map[string]interface{}{}}, + {Data: "{} \t\r\n}", ExpectedErrorMessage: "error decoding object from json: unexpected trailing data at offset 6"}, + {Data: "{} \t\r\n{}", ExpectedErrorMessage: "error decoding object from json: unexpected trailing data at offset 6"}, + {Data: "[] \t\r\n", Expected: []interface{}{}}, + {Data: "[] \t\r\n]", ExpectedErrorMessage: "error decoding array from json: unexpected trailing data at offset 6"}, + {Data: "[] \t\r\n[]", ExpectedErrorMessage: "error decoding array from json: unexpected trailing data at offset 6"}, } for _, tc := range testcases { @@ -84,7 +93,13 @@ func TestToUnstructured(t *testing.T) { rv := reflect.ValueOf(custom) result, err := TypeReflectEntryOf(rv.Type()).ToUnstructured(rv) if err != nil { - t.Fatal(err) + if tc.ExpectedErrorMessage == "" { + t.Fatal(err) + } else if got := err.Error(); got != tc.ExpectedErrorMessage { + t.Fatalf("expected error message %q but got %q", tc.ExpectedErrorMessage, got) + } + } else if tc.ExpectedErrorMessage != "" { + t.Fatalf("expected error message %q but got nil error", tc.ExpectedErrorMessage) } if !reflect.DeepEqual(result, tc.Expected) { t.Errorf("expected %#v but got %#v", tc.Expected, result) @@ -199,3 +214,142 @@ func TestTypeReflectEntryOf(t *testing.T) { }) } } + +func TestUnmarshal(t *testing.T) { + for _, tc := range []struct { + JSON string + IntoType reflect.Type + Want interface{} + WantError bool + }{ + { + JSON: "{}}", + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: map[string]interface{}{}, + WantError: true, + }, + { + JSON: `1.0`, + IntoType: reflect.TypeOf(json.Number("")), + Want: json.Number("1.0"), + }, + { + JSON: `1`, + IntoType: reflect.TypeOf(json.Number("")), + Want: json.Number("1"), + }, + { + JSON: `1.0`, + IntoType: reflect.TypeOf(float64(0)), + Want: float64(1), + }, + { + JSON: `1`, + IntoType: reflect.TypeOf(float64(0)), + Want: float64(1), + }, + { + JSON: `1.0`, + IntoType: reflect.TypeOf(int64(0)), + Want: int64(0), + WantError: true, + }, + { + JSON: `1`, + IntoType: reflect.TypeOf(int64(0)), + Want: int64(1), + }, + { + JSON: `1.0`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: float64(1), + }, + { + JSON: `1`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: int64(1), + }, + { + JSON: `[1.0,[1.0],{"":1.0}]`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: []interface{}{ + float64(1), + []interface{}{float64(1)}, + map[string]interface{}{"": float64(1)}, + }, + }, + { + JSON: `[1.0,[1.0],{"":1.0}]`, + IntoType: reflect.TypeOf([]interface{}{}), + Want: []interface{}{ + float64(1), + []interface{}{float64(1)}, + map[string]interface{}{"": float64(1)}, + }, + }, + { + JSON: `[1,[1],{"":1}]`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: []interface{}{ + int64(1), + []interface{}{int64(1)}, + map[string]interface{}{"": int64(1)}, + }, + }, + { + JSON: `[1,[1],{"":1}]`, + IntoType: reflect.TypeOf([]interface{}{}), + Want: []interface{}{ + int64(1), + []interface{}{int64(1)}, + map[string]interface{}{"": int64(1)}, + }, + }, + { + JSON: `{"x":1.0,"y":[1.0],"z":{"":1.0}}`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: map[string]interface{}{ + "x": float64(1), + "y": []interface{}{float64(1)}, + "z": map[string]interface{}{"": float64(1)}, + }, + }, + { + JSON: `{"x":1.0,"y":[1.0],"z":{"":1.0}}`, + IntoType: reflect.TypeOf(map[string]interface{}{}), + Want: map[string]interface{}{ + "x": float64(1), + "y": []interface{}{float64(1)}, + "z": map[string]interface{}{"": float64(1)}, + }, + }, + { + JSON: `{"x":1,"y":[1],"z":{"":1}}`, + IntoType: reflect.TypeOf([0]interface{}{}).Elem(), + Want: map[string]interface{}{ + "x": int64(1), + "y": []interface{}{int64(1)}, + "z": map[string]interface{}{"": int64(1)}, + }, + }, + { + JSON: `{"x":1,"y":[1],"z":{"":1}}`, + IntoType: reflect.TypeOf(map[string]interface{}{}), + Want: map[string]interface{}{ + "x": int64(1), + "y": []interface{}{int64(1)}, + "z": map[string]interface{}{"": int64(1)}, + }, + }, + } { + t.Run(fmt.Sprintf("%s into %v", tc.JSON, reflect.PointerTo(tc.IntoType)), func(t *testing.T) { + into := reflect.New(tc.IntoType) + if err := unmarshal([]byte(tc.JSON), into.Interface()); tc.WantError != (err != nil) { + t.Fatalf("unexpected error: %v", err) + } + if got := into.Elem().Interface(); !reflect.DeepEqual(tc.Want, got) { + t.Errorf("want %#v (%T), got %#v (%T)", tc.Want, tc.Want, got, got) + } + }) + } +} From 83ecba28226a9269f6d4d4941e4d816307c774b1 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Sat, 5 Oct 2024 19:47:57 +0000 Subject: [PATCH 066/105] use our own fork of go-yaml Change-Id: I87fd9303c9f582efacadad79df00b3b98a696bd0 --- fieldpath/fromvalue_test.go | 2 +- fieldpath/set_test.go | 2 +- go.mod | 5 ++--- go.sum | 4 ++-- merge/multiple_appliers_test.go | 2 +- typed/parser.go | 2 +- typed/parser_test.go | 2 +- value/equals_test.go | 2 +- value/value.go | 2 +- 9 files changed, 11 insertions(+), 12 deletions(-) diff --git a/fieldpath/fromvalue_test.go b/fieldpath/fromvalue_test.go index 1cbf0b1b..90e8b723 100644 --- a/fieldpath/fromvalue_test.go +++ b/fieldpath/fromvalue_test.go @@ -19,8 +19,8 @@ package fieldpath import ( "testing" - "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/value" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) func TestFromValue(t *testing.T) { diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index 60f97bd7..cbb645e5 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -22,8 +22,8 @@ import ( "math/rand" "testing" - "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/schema" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) type randomPathAlphabet []PathElement diff --git a/go.mod b/go.mod index 2133f8c4..e2471112 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,11 @@ module sigs.k8s.io/structured-merge-diff/v4 -require gopkg.in/yaml.v2 v2.2.8 - require ( - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.5.9 github.com/google/gofuzz v1.0.0 github.com/json-iterator/go v1.1.12 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + sigs.k8s.io/yaml v1.4.0 ) go 1.13 diff --git a/go.sum b/go.sum index a37122bb..deca704d 100644 --- a/go.sum +++ b/go.sum @@ -19,5 +19,5 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/merge/multiple_appliers_test.go b/merge/multiple_appliers_test.go index 01ce32e0..0794cda9 100644 --- a/merge/multiple_appliers_test.go +++ b/merge/multiple_appliers_test.go @@ -22,12 +22,12 @@ import ( "strings" "testing" - "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" "sigs.k8s.io/structured-merge-diff/v4/merge" "sigs.k8s.io/structured-merge-diff/v4/typed" "sigs.k8s.io/structured-merge-diff/v4/value" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) func TestMultipleAppliersSet(t *testing.T) { diff --git a/typed/parser.go b/typed/parser.go index 4258ee5b..0e9f7cc7 100644 --- a/typed/parser.go +++ b/typed/parser.go @@ -19,9 +19,9 @@ package typed import ( "fmt" - yaml "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/schema" "sigs.k8s.io/structured-merge-diff/v4/value" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) // YAMLObject is an object encoded in YAML. diff --git a/typed/parser_test.go b/typed/parser_test.go index fdb7d9fc..b81a9372 100644 --- a/typed/parser_test.go +++ b/typed/parser_test.go @@ -22,8 +22,8 @@ import ( "strings" "testing" - yaml "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/typed" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) func testdata(file string) string { diff --git a/value/equals_test.go b/value/equals_test.go index 93847a38..1f98da2f 100644 --- a/value/equals_test.go +++ b/value/equals_test.go @@ -21,8 +21,8 @@ import ( "path/filepath" "testing" - "gopkg.in/yaml.v2" "sigs.k8s.io/structured-merge-diff/v4/value" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) func testdata(file string) string { diff --git a/value/value.go b/value/value.go index ea79e3a0..f72e5cd2 100644 --- a/value/value.go +++ b/value/value.go @@ -23,7 +23,7 @@ import ( "strings" jsoniter "github.com/json-iterator/go" - "gopkg.in/yaml.v2" + yaml "sigs.k8s.io/yaml/goyaml.v2" ) var ( From c68c9ee5bc5f9b73b5785e38a6a28a6c5b654572 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Mon, 28 Oct 2024 20:02:41 -0400 Subject: [PATCH 067/105] Accept filter instead of exclude mask for ignored fields --- fieldpath/set.go | 24 +++++++++++++++++ internal/fixture/state.go | 8 +++--- merge/ignore_test.go | 56 +++++++++++++++++++-------------------- merge/update.go | 34 +++++++++++------------- typed/compare.go | 10 +++++++ 5 files changed, 81 insertions(+), 51 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index 6d182768..eb5d184f 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -503,3 +503,27 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { } return out } + +type Filter interface { + Filter(*Set) *Set +} + +func NewExcludeFilter(set *Set) Filter { + return excludeFilter{set} +} + +func NewExcludeFilterMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter { + result := make(map[APIVersion]Filter) + for k, v := range resetFields { + result[k] = excludeFilter{v} + } + return result +} + +type excludeFilter struct { + exeludeSet *Set +} + +func (t excludeFilter) Filter(set *Set) *Set { + return set.RecursiveDifference(t.exeludeSet) +} diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 5b5d4829..039125b2 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -538,8 +538,8 @@ type TestCase struct { // ReportInputOnNoop if we don't want to compare the output and // always return it. ReturnInputOnNoop bool - // IgnoredFields containing the set to ignore for every version - IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // IgnoreFilter filters out ignored fields from a fieldpath.Set. + IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter } // Test runs the test-case using the given parser and a dummy converter. @@ -572,7 +572,7 @@ func (tc TestCase) PreprocessOperations(parser Parser) error { func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) error { updaterBuilder := merge.UpdaterBuilder{ Converter: converter, - IgnoredFields: tc.IgnoredFields, + IgnoreFilter: tc.IgnoreFilter, ReturnInputOnNoop: tc.ReturnInputOnNoop, } state := State{ @@ -594,7 +594,7 @@ func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) error { updaterBuilder := merge.UpdaterBuilder{ Converter: converter, - IgnoredFields: tc.IgnoredFields, + IgnoreFilter: tc.IgnoreFilter, ReturnInputOnNoop: tc.ReturnInputOnNoop, } state := State{ diff --git a/merge/ignore_test.go b/merge/ignore_test.go index 4cf7a918..35d6b805 100644 --- a/merge/ignore_test.go +++ b/merge/ignore_test.go @@ -50,10 +50,10 @@ func TestIgnoredFields(t *testing.T) { false, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v1": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v1": fieldpath.NewExcludeFilter(_NS( _P("string"), - ), + )), }, }, "update_does_not_own_deep_ignored": { @@ -75,10 +75,10 @@ func TestIgnoredFields(t *testing.T) { false, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v1": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v1": fieldpath.NewExcludeFilter(_NS( _P("obj"), - ), + )), }, }, "apply_does_not_own_ignored": { @@ -106,10 +106,10 @@ func TestIgnoredFields(t *testing.T) { true, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v1": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v1": fieldpath.NewExcludeFilter(_NS( _P("string"), - ), + )), }, }, "apply_does_not_own_deep_ignored": { @@ -131,10 +131,10 @@ func TestIgnoredFields(t *testing.T) { true, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v1": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v1": fieldpath.NewExcludeFilter(_NS( _P("obj"), - ), + )), }, }, } @@ -148,7 +148,7 @@ func TestIgnoredFields(t *testing.T) { } } -func TestIgnoredFieldsUsesVersions(t *testing.T) { +func TestFilteredFieldsUsesVersions(t *testing.T) { tests := map[string]TestCase{ "does_use_ignored_fields_versions": { Ops: []Operation{ @@ -205,19 +205,19 @@ func TestIgnoredFieldsUsesVersions(t *testing.T) { false, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v1": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v1": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "c"), - ), - "v2": _NS( + )), + "v2": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "cc"), - ), - "v3": _NS( + )), + "v3": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "ccc"), - ), - "v4": _NS( + )), + "v4": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "cccc"), - ), + )), }, }, "update_does_not_steal_ignored": { @@ -273,10 +273,10 @@ func TestIgnoredFieldsUsesVersions(t *testing.T) { false, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v2": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v2": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "c"), - ), + )), }, }, "apply_does_not_steal_ignored": { @@ -332,10 +332,10 @@ func TestIgnoredFieldsUsesVersions(t *testing.T) { false, ), }, - IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ - "v2": _NS( + IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ + "v2": fieldpath.NewExcludeFilter(_NS( _P("mapOfMapsRecursive", "c"), - ), + )), }, }, } diff --git a/merge/update.go b/merge/update.go index d5a977d6..34ab2d6f 100644 --- a/merge/update.go +++ b/merge/update.go @@ -15,7 +15,6 @@ package merge import ( "fmt" - "sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/typed" "sigs.k8s.io/structured-merge-diff/v4/value" @@ -31,8 +30,8 @@ type Converter interface { // UpdateBuilder allows you to create a new Updater by exposing all of // the options and setting them once. type UpdaterBuilder struct { - Converter Converter - IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + Converter Converter + IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter // Stop comparing the new object with old object after applying. // This was initially used to avoid spurious etcd update, but @@ -46,7 +45,7 @@ type UpdaterBuilder struct { func (u *UpdaterBuilder) BuildUpdater() *Updater { return &Updater{ Converter: u.Converter, - IgnoredFields: u.IgnoredFields, + IgnoreFilter: u.IgnoreFilter, returnInputOnNoop: u.ReturnInputOnNoop, } } @@ -58,7 +57,7 @@ type Updater struct { Converter Converter // Deprecated: This will eventually become private. - IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter returnInputOnNoop bool } @@ -72,7 +71,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa } versions := map[fieldpath.APIVersion]*typed.Comparison{ - version: compare.ExcludeFields(s.IgnoredFields[version]), + version: compare.FilterFields(s.IgnoreFilter[version]), } for manager, managerSet := range managers { @@ -102,7 +101,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa if err != nil { return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions[managerSet.APIVersion()] = compare.ExcludeFields(s.IgnoredFields[managerSet.APIVersion()]) + versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) } conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added)) @@ -154,13 +153,14 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp if _, ok := managers[manager]; !ok { managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false) } - - ignored := s.IgnoredFields[version] - if ignored == nil { - ignored = fieldpath.NewSet() + set := managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added) + ignoreFilter := s.IgnoreFilter[version] + if ignoreFilter != nil { + set = ignoreFilter.Filter(set) } + managers[manager] = fieldpath.NewVersionedSet( - managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added).RecursiveDifference(ignored), + set, version, false, ) @@ -189,13 +189,9 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err) } - ignored := s.IgnoredFields[version] - if ignored != nil { - set = set.RecursiveDifference(ignored) - // TODO: is this correct. If we don't remove from lastSet pruning might remove the fields? - if lastSet != nil { - lastSet.Set().RecursiveDifference(ignored) - } + ignoreFilter := s.IgnoreFilter[version] + if ignoreFilter != nil { + set = ignoreFilter.Filter(set) } managers[manager] = fieldpath.NewVersionedSet(set, version, true) newObject, err = s.prune(newObject, managers, manager, lastSet) diff --git a/typed/compare.go b/typed/compare.go index ed483cbb..5fffa5e2 100644 --- a/typed/compare.go +++ b/typed/compare.go @@ -72,6 +72,16 @@ func (c *Comparison) ExcludeFields(fields *fieldpath.Set) *Comparison { return c } +func (c *Comparison) FilterFields(filter fieldpath.Filter) *Comparison { + if filter == nil { + return c + } + c.Removed = filter.Filter(c.Removed) + c.Modified = filter.Filter(c.Modified) + c.Added = filter.Filter(c.Added) + return c +} + type compareWalker struct { lhs value.Value rhs value.Value From 083ce27d38181e56449fcd4b75f331cefdfb3919 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Tue, 29 Oct 2024 20:24:52 -0400 Subject: [PATCH 068/105] Add field path pattern matching --- fieldpath/set.go | 164 ++++++++++++++++++++++++++++++++++++++++-- fieldpath/set_test.go | 93 ++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 4 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index eb5d184f..94235895 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -17,6 +17,7 @@ limitations under the License. package fieldpath import ( + "fmt" "sort" "strings" @@ -136,6 +137,111 @@ func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) } } +// MustPrefixPattern is the same as PrefixPattern except it panics if parts can't be +// turned into a SetPattern. +func MustPrefixPattern(parts ...interface{}) *SetPattern { + result, err := PrefixPattern(parts...) + if err != nil { + panic(err) + } + return result +} + +// PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of path parts. +// The parts may be PathPatterns, PathElements, strings (for field names) or ints (for array indices). +// `MatchAnyPathElement()` may be used to "wildcard" match any PathElement at that position in the field path. +func PrefixPattern(parts ...interface{}) (*SetPattern, error) { + current := MatchAnySet() // match all field patch suffixes + for i := len(parts) - 1; i >= 0; i-- { + part := parts[i] + var pattern PathPattern + switch t := part.(type) { + case PathPattern: + pattern = t + case PathElement: + pattern = PathPattern{PathElement: t} + case string: + pattern = PathPattern{PathElement: PathElement{FieldName: &t}} + case int: + pattern = PathPattern{PathElement: PathElement{Index: &t}} + default: + return nil, fmt.Errorf("unexpected type %T", t) + } + current = &SetPattern{ + Members: []*MemberSetPattern{{ + Path: pattern, + Child: current, + }}, + } + } + return current, nil +} + +// MatchAnyPathElement returns a PathPattern that matches any path element. +func MatchAnyPathElement() PathPattern { + return PathPattern{Wildcard: true} +} + +// MatchAnySet returns a SetPattern that matches any set. +func MatchAnySet() *SetPattern { + return &SetPattern{Wildcard: true} +} + +// SetPattern defines a pattern that matches fields in a Set. +// SetPattern is structured much like a Set but with wildcard support. +type SetPattern struct { + // Wildcard indicates that all members and children are matched. + // If set, the Members and Children fields are ignored. + Wildcard bool + // Members provides patterns to match the Members of a Set. + // If any PatchPattern is a wildcard, then all members of a Set are matched. + // Otherwise, if any PathPattern is Equal to a member of a Set, that member is matched. + Members []*MemberSetPattern +} + +// MemberSetPattern defines a pattern that matches the Members of a Set. +// MemberSetPattern is structured much like the elements of a SetNodeMap, but with wildcard support. +type MemberSetPattern struct { + // Path provides a pattern to match Members of a Set. + // If Path is a wildcard, all Members of a Set are matched. + // Otherwise, the Member of a Set with a path that is Equal to this Path is matched. + Path PathPattern + + // Child provides a pattern to use for Member of a Set that were matched by this MemberSetPattern's Path. + Child *SetPattern +} + +// PathPattern defined a match pattern for a PathElement. +type PathPattern struct { + // Wildcard indicates that all PathElements are matched by this pattern. + // If set, PathElement is ignored. + Wildcard bool + + // PathElement matches another PathElement if it is Equal to this PathElement. + PathElement +} + +// FilterByPattern returns a Set with only fields that match the pattern. +func (s *Set) FilterByPattern(pattern *SetPattern) *Set { + if pattern.Wildcard { + return s + } + + members := PathElementSet{} + for _, m := range s.Members.members { + for _, pm := range pattern.Members { + if pm.Path.Wildcard || pm.Path.PathElement.Equals(m) { + members.Insert(m) + break + } + } + } + return &Set{ + Members: members, + Children: *s.Children.FilterByPattern(pattern), + } +} + // Size returns the number of members of the set. func (s *Set) Size() int { return s.Members.Size() + s.Children.Size() @@ -476,6 +582,33 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty } } +// FilterByPattern returns a set that is filtered by the pattern. +func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap { + if pattern.Wildcard { + return s + } + + var out sortedSetNode + for _, member := range s.members { + for _, c := range pattern.Members { + if c.Path.Wildcard || c.Path.PathElement.Equals(member.pathElement) { + childSet := member.set.FilterByPattern(c.Child) + if childSet.Size() > 0 { + out = append(out, setNode{ + pathElement: member.pathElement, + set: childSet, + }) + } + break + } + } + } + + return &SetNodeMap{ + members: out, + } +} + // Iterate calls f for each PathElement in the set. func (s *SetNodeMap) Iterate(f func(PathElement)) { for _, n := range s.members { @@ -504,14 +637,23 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { return out } +// Filter defines an interface for filtering Set. +// NewExcludeFilter can be used to create a filter that removes fields at the +// excluded field paths. +// NewPatternFilter can be used to create a filter that removes all fields except +// the fields that match a field path pattern. PrefixPattern and MustPrefixPattern +// can help create field path patterns. type Filter interface { + // Filter returns a filtered copy of the set. Filter(*Set) *Set } -func NewExcludeFilter(set *Set) Filter { - return excludeFilter{set} +// NewExcludeFilter returns a filter that removes field paths in the exclude set. +func NewExcludeFilter(exclude *Set) Filter { + return excludeFilter{exclude} } +// NewExcludeFilterMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters. func NewExcludeFilterMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter { result := make(map[APIVersion]Filter) for k, v := range resetFields { @@ -521,9 +663,23 @@ func NewExcludeFilterMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter } type excludeFilter struct { - exeludeSet *Set + excludeSet *Set } func (t excludeFilter) Filter(set *Set) *Set { - return set.RecursiveDifference(t.exeludeSet) + return set.RecursiveDifference(t.excludeSet) +} + +// NewPatternFilter returns a filter that only includes field paths that match the pattern. +// PrefixPattern and MustPrefixPattern can help create basic SetPatterns. +func NewPatternFilter(pattern *SetPattern) Filter { + return patternFilter{pattern} +} + +type patternFilter struct { + pattern *SetPattern +} + +func (pf patternFilter) Filter(set *Set) *Set { + return set.FilterByPattern(pf.pattern) } diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index cbb645e5..b3e9aa65 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -755,3 +755,96 @@ func TestSetNodeMapIterate(t *testing.T) { } } } + +func TestFilterByPattern(t *testing.T) { + testCases := []struct { + name string + input *Set + expect *Set + }{ + { + name: "exact match", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0, "resources"), + MakePathOrDie("spec", "containers", 0, "resources", "limits"), + MakePathOrDie("spec", "containers", 0, "resources", "limits", "cpu"), + MakePathOrDie("spec", "containers", 0, "resources", "requests"), + MakePathOrDie("spec", "containers", 0, "resources", "requests", "cpu"), + MakePathOrDie("spec", "containers", 0, "resources", "claims"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "name"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "request"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "name"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "request"), + MakePathOrDie("spec", "containers", 1, "resources"), + MakePathOrDie("spec", "containers", 1, "resources", "limits"), + MakePathOrDie("spec", "containers", 1, "resources", "limits", "cpu"), + ), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0, "resources"), + MakePathOrDie("spec", "containers", 0, "resources", "limits"), + MakePathOrDie("spec", "containers", 0, "resources", "limits", "cpu"), + MakePathOrDie("spec", "containers", 0, "resources", "requests"), + MakePathOrDie("spec", "containers", 0, "resources", "requests", "cpu"), + MakePathOrDie("spec", "containers", 0, "resources", "claims"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "name"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "request"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "name"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "request"), + MakePathOrDie("spec", "containers", 1, "resources"), + MakePathOrDie("spec", "containers", 1, "resources", "limits"), + MakePathOrDie("spec", "containers", 1, "resources", "limits", "cpu"), + ), + }, + { + name: "filter status and metadata", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("status"), + MakePathOrDie("metadata"), + ), + expect: NewSet( + MakePathOrDie("spec"), + ), + }, + { + name: "filter non-container spec fields", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "volumes"), + MakePathOrDie("spec", "hostNetwork"), + ), + expect: NewSet( + MakePathOrDie("spec"), + ), + }, + { + name: "filter non-resource container fields", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0, "image"), + MakePathOrDie("spec", "containers", 0, "workingDir"), + MakePathOrDie("spec", "containers", 0, "resources"), + ), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0, "resources"), + ), + }, + } + + for _, tc := range testCases { + filter := NewPatternFilter(MustPrefixPattern("spec", "containers", MatchAnyPathElement(), "resources")) + t.Run(tc.name, func(t *testing.T) { + filtered := filter.Filter(tc.input) + if !filtered.Equals(tc.expect) { + t.Errorf("Expected:\n%v\n\nbut got:\n%v", tc.expect, filtered) + } + }) + } +} From 1206de614bb9ae95ce82d1da804c6de3de255ddb Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 30 Oct 2024 10:23:09 -0400 Subject: [PATCH 069/105] Clean up comments --- fieldpath/set.go | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index 94235895..f92af880 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -147,9 +147,9 @@ func MustPrefixPattern(parts ...interface{}) *SetPattern { return result } -// PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of path parts. -// The parts may be PathPatterns, PathElements, strings (for field names) or ints (for array indices). -// `MatchAnyPathElement()` may be used to "wildcard" match any PathElement at that position in the field path. +// PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of pattern path parts. +// The pattern parts may be PathPatterns, PathElements, strings (for field names) or ints (for array indices). +// `MatchAnyPathElement()` may be used as a pattern path part to wildcard match a field path part. func PrefixPattern(parts ...interface{}) (*SetPattern, error) { current := MatchAnySet() // match all field patch suffixes for i := len(parts) - 1; i >= 0; i-- { @@ -190,24 +190,25 @@ func MatchAnySet() *SetPattern { // SetPattern defines a pattern that matches fields in a Set. // SetPattern is structured much like a Set but with wildcard support. type SetPattern struct { - // Wildcard indicates that all members and children are matched. - // If set, the Members and Children fields are ignored. + // Wildcard indicates that all members and children are included in the match. + // If set, the Members field is ignored. Wildcard bool - // Members provides patterns to match the Members of a Set. - // If any PatchPattern is a wildcard, then all members of a Set are matched. - // Otherwise, if any PathPattern is Equal to a member of a Set, that member is matched. + // Members provides patterns to match the members of a Set. Members []*MemberSetPattern } -// MemberSetPattern defines a pattern that matches the Members of a Set. -// MemberSetPattern is structured much like the elements of a SetNodeMap, but with wildcard support. +// MemberSetPattern defines a pattern that matches the members of a Set. +// MemberSetPattern is structured much like the elements of a SetNodeMap, but +// with wildcard support. type MemberSetPattern struct { - // Path provides a pattern to match Members of a Set. - // If Path is a wildcard, all Members of a Set are matched. - // Otherwise, the Member of a Set with a path that is Equal to this Path is matched. + // Path provides a pattern to match members of a Set. + // If Path is a wildcard, all members of a Set are included in the match. + // Otherwise, if any Path is Equal to a member of a Set, that member is + // included in the match and the children of that member are matched + // against the Child pattern. Path PathPattern - // Child provides a pattern to use for Member of a Set that were matched by this MemberSetPattern's Path. + // Child provides a pattern to use for the children of matched members of a Set. Child *SetPattern } @@ -217,11 +218,12 @@ type PathPattern struct { // If set, PathElement is ignored. Wildcard bool - // PathElement matches another PathElement if it is Equal to this PathElement. + // PathElement indicates that a PathElement is matched if it is Equal + // to this PathElement. PathElement } -// FilterByPattern returns a Set with only fields that match the pattern. +// FilterByPattern returns a Set with only the field paths that match the pattern. func (s *Set) FilterByPattern(pattern *SetPattern) *Set { if pattern.Wildcard { return s @@ -582,7 +584,7 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty } } -// FilterByPattern returns a set that is filtered by the pattern. +// FilterByPattern returns a SetNodeMap with only the field paths that match the pattern. func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap { if pattern.Wildcard { return s @@ -637,12 +639,12 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { return out } -// Filter defines an interface for filtering Set. -// NewExcludeFilter can be used to create a filter that removes fields at the +// Filter defines an interface for filtering a set. +// NewExcludeFilter can be used to create a filter that removes // excluded field paths. // NewPatternFilter can be used to create a filter that removes all fields except // the fields that match a field path pattern. PrefixPattern and MustPrefixPattern -// can help create field path patterns. +// can be used to define field path patterns. type Filter interface { // Filter returns a filtered copy of the set. Filter(*Set) *Set From 1311e4d03f19bf01ef0679c2e2940763840eeaec Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 30 Oct 2024 13:55:11 -0400 Subject: [PATCH 070/105] Add support for more field path pattern types, clarify comments --- fieldpath/set.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index f92af880..a16fc23a 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -18,6 +18,7 @@ package fieldpath import ( "fmt" + "sigs.k8s.io/structured-merge-diff/v4/value" "sort" "strings" @@ -148,8 +149,14 @@ func MustPrefixPattern(parts ...interface{}) *SetPattern { } // PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of pattern path parts. -// The pattern parts may be PathPatterns, PathElements, strings (for field names) or ints (for array indices). -// `MatchAnyPathElement()` may be used as a pattern path part to wildcard match a field path part. +// The pattern parts may any of: +// +// - PathPattern - for wildcards, `MatchAnyPathElement()` can be used as well. +// - PathElement - for any path element +// - value.FieldList - for associative list keys +// - value.Value - for scalar list elements +// - string - For field names +// - int - for array indices func PrefixPattern(parts ...interface{}) (*SetPattern, error) { current := MatchAnySet() // match all field patch suffixes for i := len(parts) - 1; i >= 0; i-- { @@ -160,6 +167,13 @@ func PrefixPattern(parts ...interface{}) (*SetPattern, error) { pattern = t case PathElement: pattern = PathPattern{PathElement: t} + case *value.FieldList: + if len(*t) == 0 { + return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)") + } + pattern = PathPattern{PathElement: PathElement{Key: t}} + case value.Value: + pattern = PathPattern{PathElement: PathElement{Value: &t}} case string: pattern = PathPattern{PathElement: PathElement{FieldName: &t}} case int: @@ -639,7 +653,7 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { return out } -// Filter defines an interface for filtering a set. +// Filter defines an interface for excluding field paths from a set. // NewExcludeFilter can be used to create a filter that removes // excluded field paths. // NewPatternFilter can be used to create a filter that removes all fields except From f395ded435c48b071762bb3af322759a5fd956b3 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 30 Oct 2024 22:11:22 -0400 Subject: [PATCH 071/105] Apply feedback --- fieldpath/set.go | 157 ++++++++++++++++++++++++-------------- fieldpath/set_test.go | 172 ++++++++++++++++++++++++++++++++++++++++-- merge/ignore_test.go | 20 ++--- 3 files changed, 277 insertions(+), 72 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index a16fc23a..c35b2062 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -138,51 +138,51 @@ func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) } } -// MustPrefixPattern is the same as PrefixPattern except it panics if parts can't be -// turned into a SetPattern. -func MustPrefixPattern(parts ...interface{}) *SetPattern { - result, err := PrefixPattern(parts...) +// MakePrefixMatcherOrDie is the same as PrefixMatcher except it panics if parts can't be +// turned into a SetMatcher. +func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher { + result, err := PrefixMatcher(parts...) if err != nil { panic(err) } return result } -// PrefixPattern creates a SetPattern that matches all field paths prefixed by the given list of pattern path parts. +// PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of pattern path parts. // The pattern parts may any of: // -// - PathPattern - for wildcards, `MatchAnyPathElement()` can be used as well. +// - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well. // - PathElement - for any path element // - value.FieldList - for associative list keys // - value.Value - for scalar list elements // - string - For field names // - int - for array indices -func PrefixPattern(parts ...interface{}) (*SetPattern, error) { - current := MatchAnySet() // match all field patch suffixes +func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) { + current := MatchAnySet() // match all field path suffixes for i := len(parts) - 1; i >= 0; i-- { part := parts[i] - var pattern PathPattern + var pattern PathElementMatcher switch t := part.(type) { - case PathPattern: + case PathElementMatcher: pattern = t case PathElement: - pattern = PathPattern{PathElement: t} + pattern = PathElementMatcher{PathElement: t} case *value.FieldList: if len(*t) == 0 { return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)") } - pattern = PathPattern{PathElement: PathElement{Key: t}} + pattern = PathElementMatcher{PathElement: PathElement{Key: t}} case value.Value: - pattern = PathPattern{PathElement: PathElement{Value: &t}} + pattern = PathElementMatcher{PathElement: PathElement{Value: &t}} case string: - pattern = PathPattern{PathElement: PathElement{FieldName: &t}} + pattern = PathElementMatcher{PathElement: PathElement{FieldName: &t}} case int: - pattern = PathPattern{PathElement: PathElement{Index: &t}} + pattern = PathElementMatcher{PathElement: PathElement{Index: &t}} default: return nil, fmt.Errorf("unexpected type %T", t) } - current = &SetPattern{ - Members: []*MemberSetPattern{{ + current = &SetMatcher{ + Members: []*SetMemberMatcher{{ Path: pattern, Child: current, }}, @@ -191,43 +191,80 @@ func PrefixPattern(parts ...interface{}) (*SetPattern, error) { return current, nil } -// MatchAnyPathElement returns a PathPattern that matches any path element. -func MatchAnyPathElement() PathPattern { - return PathPattern{Wildcard: true} +// MatchAnyPathElement returns a PathElementMatcher that matches any path element. +func MatchAnyPathElement() PathElementMatcher { + return PathElementMatcher{Wildcard: true} } -// MatchAnySet returns a SetPattern that matches any set. -func MatchAnySet() *SetPattern { - return &SetPattern{Wildcard: true} +// MatchAnySet returns a SetMatcher that matches any set. +func MatchAnySet() *SetMatcher { + return &SetMatcher{Wildcard: true} } -// SetPattern defines a pattern that matches fields in a Set. -// SetPattern is structured much like a Set but with wildcard support. -type SetPattern struct { +// SetMatcher defines a pattern that matches fields in a Set. +// SetMatcher is structured much like a Set but with wildcard support. +type SetMatcher struct { // Wildcard indicates that all members and children are included in the match. // If set, the Members field is ignored. Wildcard bool // Members provides patterns to match the members of a Set. - Members []*MemberSetPattern + Members []*SetMemberMatcher +} + +// Merge merges two SetMatchers into a single SetMatcher that matches the field paths of both SetMatchers. +// During the merge, Members of s2 with the same PathElementMatcher as a member of s are merged into the member of s. +// All other members of s2 are appended to the resulting member list in their original relative order. +// When members are merged, the child SetMatchers are merged by calling this function recursively. +func (s *SetMatcher) Merge(s2 *SetMatcher) *SetMatcher { + if s.Wildcard || s2.Wildcard { + return &SetMatcher{Wildcard: true} + } + + // TODO: Optimize. This is O(n^2). In practice, usually a single member is being inserted at a time, + // but that's still O(n). Sorting would help a ton. + var unionedMembers []*SetMemberMatcher + for _, m := range s.Members { + for _, m2 := range s2.Members { + if m.Path.PathElement.Equals(m2.Path.PathElement) && m.Path.Wildcard == m2.Path.Wildcard { + unionedMembers = append(unionedMembers, &SetMemberMatcher{ + Path: m.Path, + Child: m.Child.Merge(m2.Child), + }) + } else { + unionedMembers = append(unionedMembers, m) + } + } + } + for _, m2 := range s2.Members { + for _, existing := range unionedMembers { + if !m2.Path.PathElement.Equals(existing.Path.PathElement) { + unionedMembers = append(unionedMembers, m2) + } + } + } + + return &SetMatcher{ + Members: unionedMembers, + } } -// MemberSetPattern defines a pattern that matches the members of a Set. -// MemberSetPattern is structured much like the elements of a SetNodeMap, but +// SetMemberMatcher defines a pattern that matches the members of a Set. +// SetMemberMatcher is structured much like the elements of a SetNodeMap, but // with wildcard support. -type MemberSetPattern struct { +type SetMemberMatcher struct { // Path provides a pattern to match members of a Set. // If Path is a wildcard, all members of a Set are included in the match. // Otherwise, if any Path is Equal to a member of a Set, that member is // included in the match and the children of that member are matched // against the Child pattern. - Path PathPattern + Path PathElementMatcher // Child provides a pattern to use for the children of matched members of a Set. - Child *SetPattern + Child *SetMatcher } -// PathPattern defined a match pattern for a PathElement. -type PathPattern struct { +// PathElementMatcher defined a match pattern for a PathElement. +type PathElementMatcher struct { // Wildcard indicates that all PathElements are matched by this pattern. // If set, PathElement is ignored. Wildcard bool @@ -237,8 +274,8 @@ type PathPattern struct { PathElement } -// FilterByPattern returns a Set with only the field paths that match the pattern. -func (s *Set) FilterByPattern(pattern *SetPattern) *Set { +// FilterIncludeMatches returns a Set with only the field paths that match. +func (s *Set) FilterIncludeMatches(pattern *SetMatcher) *Set { if pattern.Wildcard { return s } @@ -254,7 +291,7 @@ func (s *Set) FilterByPattern(pattern *SetPattern) *Set { } return &Set{ Members: members, - Children: *s.Children.FilterByPattern(pattern), + Children: *s.Children.FilterIncludeMatches(pattern), } } @@ -598,8 +635,8 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty } } -// FilterByPattern returns a SetNodeMap with only the field paths that match the pattern. -func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap { +// FilterIncludeMatches returns a SetNodeMap with only the field paths that match the pattern. +func (s *SetNodeMap) FilterIncludeMatches(pattern *SetMatcher) *SetNodeMap { if pattern.Wildcard { return s } @@ -608,7 +645,7 @@ func (s *SetNodeMap) FilterByPattern(pattern *SetPattern) *SetNodeMap { for _, member := range s.members { for _, c := range pattern.Members { if c.Path.Wildcard || c.Path.PathElement.Equals(member.pathElement) { - childSet := member.set.FilterByPattern(c.Child) + childSet := member.set.FilterIncludeMatches(c.Child) if childSet.Size() > 0 { out = append(out, setNode{ pathElement: member.pathElement, @@ -654,23 +691,23 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { } // Filter defines an interface for excluding field paths from a set. -// NewExcludeFilter can be used to create a filter that removes -// excluded field paths. -// NewPatternFilter can be used to create a filter that removes all fields except -// the fields that match a field path pattern. PrefixPattern and MustPrefixPattern +// NewExcludeSetFilter can be used to create a filter that removes +// specific field paths and all of their children. +// NewIncludeMatcherFilter can be used to create a filter that removes all fields except +// the fields that match a field path pattern. PrefixMatcher and MakePrefixMatcherOrDie // can be used to define field path patterns. type Filter interface { // Filter returns a filtered copy of the set. Filter(*Set) *Set } -// NewExcludeFilter returns a filter that removes field paths in the exclude set. -func NewExcludeFilter(exclude *Set) Filter { +// NewExcludeSetFilter returns a filter that removes field paths in the exclude set. +func NewExcludeSetFilter(exclude *Set) Filter { return excludeFilter{exclude} } -// NewExcludeFilterMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters. -func NewExcludeFilterMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter { +// NewExcludeFilterSetMap converts a map of APIVersion to exclude set to a map of APIVersion to exclude filters. +func NewExcludeFilterSetMap(resetFields map[APIVersion]*Set) map[APIVersion]Filter { result := make(map[APIVersion]Filter) for k, v := range resetFields { result[k] = excludeFilter{v} @@ -686,16 +723,24 @@ func (t excludeFilter) Filter(set *Set) *Set { return set.RecursiveDifference(t.excludeSet) } -// NewPatternFilter returns a filter that only includes field paths that match the pattern. -// PrefixPattern and MustPrefixPattern can help create basic SetPatterns. -func NewPatternFilter(pattern *SetPattern) Filter { - return patternFilter{pattern} +// NewIncludeMatcherFilter returns a filter that only includes field paths that match the pattern. +// PrefixMatcher and MakePrefixMatcherOrDie can help create basic SetPatterns. +func NewIncludeMatcherFilter(patterns ...*SetMatcher) Filter { + if len(patterns) == 0 { + return includeMatcherFilter{&SetMatcher{Wildcard: true}} + } + pattern := patterns[0] + for i := 1; i < len(patterns); i++ { + pattern = pattern.Merge(patterns[i]) + } + + return includeMatcherFilter{pattern} } -type patternFilter struct { - pattern *SetPattern +type includeMatcherFilter struct { + pattern *SetMatcher } -func (pf patternFilter) Filter(set *Set) *Set { - return set.FilterByPattern(pf.pattern) +func (pf includeMatcherFilter) Filter(set *Set) *Set { + return set.FilterIncludeMatches(pf.pattern) } diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index b3e9aa65..8122dd28 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "math/rand" + "sigs.k8s.io/structured-merge-diff/v4/value" "testing" "sigs.k8s.io/structured-merge-diff/v4/schema" @@ -761,9 +762,10 @@ func TestFilterByPattern(t *testing.T) { name string input *Set expect *Set + filter Filter }{ { - name: "exact match", + name: "container resize fields: exact match", input: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), @@ -781,6 +783,7 @@ func TestFilterByPattern(t *testing.T) { MakePathOrDie("spec", "containers", 1, "resources", "limits"), MakePathOrDie("spec", "containers", 1, "resources", "limits", "cpu"), ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "containers", MatchAnyPathElement(), "resources")), expect: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), @@ -800,29 +803,31 @@ func TestFilterByPattern(t *testing.T) { ), }, { - name: "filter status and metadata", + name: "container resize fields: filter status and metadata", input: NewSet( MakePathOrDie("spec"), MakePathOrDie("status"), MakePathOrDie("metadata"), ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "containers", MatchAnyPathElement(), "resources")), expect: NewSet( MakePathOrDie("spec"), ), }, { - name: "filter non-container spec fields", + name: "container resize fields: filter non-container spec fields", input: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "volumes"), MakePathOrDie("spec", "hostNetwork"), ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "containers", MatchAnyPathElement(), "resources")), expect: NewSet( MakePathOrDie("spec"), ), }, { - name: "filter non-resource container fields", + name: "container resize fields: filter non-resource container fields", input: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), @@ -830,18 +835,173 @@ func TestFilterByPattern(t *testing.T) { MakePathOrDie("spec", "containers", 0, "workingDir"), MakePathOrDie("spec", "containers", 0, "resources"), ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "containers", MatchAnyPathElement(), "resources")), expect: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), MakePathOrDie("spec", "containers", 0, "resources"), ), }, + { + name: "filter listMap key", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }, "field"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("valueX")}, + {Name: "key2", Value: value.NewValueInterface("valueY")}, + }, "field"), + ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "listMap", &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + })), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }, "field"), + ), + }, + { + name: "filter listMap key", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }, "field"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("valueX")}, + {Name: "key2", Value: value.NewValueInterface("valueY")}, + }, "field"), + ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "listMap", &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + })), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }), + MakePathOrDie("spec", "listMap", + &value.FieldList{ + {Name: "key1", Value: value.NewValueInterface("value1")}, + {Name: "key2", Value: value.NewValueInterface("value2")}, + }, "field"), + ), + }, + { + name: "filter value", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "set", value.NewValueInterface("v1")), + MakePathOrDie("spec", "set", value.NewValueInterface("v2")), + ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "set", value.NewValueInterface("v1"))), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "set", value.NewValueInterface("v1")), + ), + }, + { + name: "filter by index", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1, "value"), + ), + filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "list", 1, "value")), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list", 1, "value"), + ), + }, + { + name: "multiple index matchers", + input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1, "value"), + ), + filter: NewIncludeMatcherFilter( + MakePrefixMatcherOrDie("spec", "list", 0, "value"), + MakePrefixMatcherOrDie("spec", "list", 1, "value"), + ), + expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1, "value"), + ), + }, + { + name: "multiple field matchers", + input: NewSet( + MakePathOrDie("spec", "f1", "f11"), + MakePathOrDie("spec", "f2", "f21"), + MakePathOrDie("spec", "f3", "f31"), + ), + filter: NewIncludeMatcherFilter( + MakePrefixMatcherOrDie("spec", "f1"), + MakePrefixMatcherOrDie("spec", "f3"), + ), + expect: NewSet( + MakePathOrDie("spec", "f1", "f11"), + MakePathOrDie("spec", "f3", "f31"), + ), + }, + { + name: "wildcard takes precedence", + input: NewSet( + MakePathOrDie("spec", "list", 0, "f1"), + MakePathOrDie("spec", "list", 0, "f2"), + MakePathOrDie("spec", "list", 1, "f1"), + MakePathOrDie("spec", "list", 1, "f2"), + MakePathOrDie("spec", "list", 2, "f1"), + MakePathOrDie("spec", "list", 2, "f2"), + ), + filter: NewIncludeMatcherFilter( + MakePrefixMatcherOrDie("spec", "list", MatchAnyPathElement(), "f1"), // wildcard matches first and runs + MakePrefixMatcherOrDie("spec", "list", 1, "f2"), // already matched by wildcard so this is ignored + ), + expect: NewSet( + MakePathOrDie("spec", "list", 0, "f1"), + MakePathOrDie("spec", "list", 1, "f1"), + MakePathOrDie("spec", "list", 2, "f1"), + ), + }, } for _, tc := range testCases { - filter := NewPatternFilter(MustPrefixPattern("spec", "containers", MatchAnyPathElement(), "resources")) t.Run(tc.name, func(t *testing.T) { - filtered := filter.Filter(tc.input) + filtered := tc.filter.Filter(tc.input) if !filtered.Equals(tc.expect) { t.Errorf("Expected:\n%v\n\nbut got:\n%v", tc.expect, filtered) } diff --git a/merge/ignore_test.go b/merge/ignore_test.go index 35d6b805..5a8b4200 100644 --- a/merge/ignore_test.go +++ b/merge/ignore_test.go @@ -51,7 +51,7 @@ func TestIgnoredFields(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v1": fieldpath.NewExcludeFilter(_NS( + "v1": fieldpath.NewExcludeSetFilter(_NS( _P("string"), )), }, @@ -76,7 +76,7 @@ func TestIgnoredFields(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v1": fieldpath.NewExcludeFilter(_NS( + "v1": fieldpath.NewExcludeSetFilter(_NS( _P("obj"), )), }, @@ -107,7 +107,7 @@ func TestIgnoredFields(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v1": fieldpath.NewExcludeFilter(_NS( + "v1": fieldpath.NewExcludeSetFilter(_NS( _P("string"), )), }, @@ -132,7 +132,7 @@ func TestIgnoredFields(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v1": fieldpath.NewExcludeFilter(_NS( + "v1": fieldpath.NewExcludeSetFilter(_NS( _P("obj"), )), }, @@ -206,16 +206,16 @@ func TestFilteredFieldsUsesVersions(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v1": fieldpath.NewExcludeFilter(_NS( + "v1": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "c"), )), - "v2": fieldpath.NewExcludeFilter(_NS( + "v2": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "cc"), )), - "v3": fieldpath.NewExcludeFilter(_NS( + "v3": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "ccc"), )), - "v4": fieldpath.NewExcludeFilter(_NS( + "v4": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "cccc"), )), }, @@ -274,7 +274,7 @@ func TestFilteredFieldsUsesVersions(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v2": fieldpath.NewExcludeFilter(_NS( + "v2": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "c"), )), }, @@ -333,7 +333,7 @@ func TestFilteredFieldsUsesVersions(t *testing.T) { ), }, IgnoreFilter: map[fieldpath.APIVersion]fieldpath.Filter{ - "v2": fieldpath.NewExcludeFilter(_NS( + "v2": fieldpath.NewExcludeSetFilter(_NS( _P("mapOfMapsRecursive", "c"), )), }, From a8ac1f52b61e8506de6d4914931e7eafe8993b1d Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Thu, 31 Oct 2024 10:06:19 -0400 Subject: [PATCH 072/105] Optimize merge, make wildcards always take precedence --- fieldpath/set.go | 158 +++++++++++++++++++++++++----------------- fieldpath/set_test.go | 4 +- 2 files changed, 96 insertions(+), 66 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index c35b2062..e07d0f3a 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -148,8 +148,8 @@ func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher { return result } -// PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of pattern path parts. -// The pattern parts may any of: +// PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of matcher path parts. +// The matcher parts may any of: // // - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well. // - PathElement - for any path element @@ -182,7 +182,7 @@ func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) { return nil, fmt.Errorf("unexpected type %T", t) } current = &SetMatcher{ - Members: []*SetMemberMatcher{{ + members: []*SetMemberMatcher{{ Path: pattern, Child: current, }}, @@ -198,74 +198,81 @@ func MatchAnyPathElement() PathElementMatcher { // MatchAnySet returns a SetMatcher that matches any set. func MatchAnySet() *SetMatcher { - return &SetMatcher{Wildcard: true} + return &SetMatcher{wildcard: true} } -// SetMatcher defines a pattern that matches fields in a Set. +// NewSetMatcher returns a new SetMatcher. +// Wildcard members take precedent over non-wildcard members; +// all non-wildcard members are ignored if there is a wildcard members. +func NewSetMatcher(wildcard bool, members ...*SetMemberMatcher) *SetMatcher { + sort.Sort(sortedMemberMatcher(members)) + return &SetMatcher{wildcard: wildcard, members: members} +} + +// SetMatcher defines a matcher that matches fields in a Set. // SetMatcher is structured much like a Set but with wildcard support. type SetMatcher struct { - // Wildcard indicates that all members and children are included in the match. - // If set, the Members field is ignored. - Wildcard bool - // Members provides patterns to match the members of a Set. - Members []*SetMemberMatcher + // wildcard indicates that all members and children are included in the match. + // If set, the members field is ignored. + wildcard bool + // members provides patterns to match the members of a Set. + // Wildcard members are sorted before non-wildcards and take precedent over + // non-wildcard members. + members sortedMemberMatcher +} + +type sortedMemberMatcher []*SetMemberMatcher + +func (s sortedMemberMatcher) Len() int { return len(s) } +func (s sortedMemberMatcher) Less(i, j int) bool { return s[i].Path.Less(s[j].Path) } +func (s sortedMemberMatcher) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sortedMemberMatcher) Find(p PathElementMatcher) (location int, ok bool) { + return sort.Find(len(s), func(i int) int { + return s[i].Path.Compare(p) + }) } -// Merge merges two SetMatchers into a single SetMatcher that matches the field paths of both SetMatchers. -// During the merge, Members of s2 with the same PathElementMatcher as a member of s are merged into the member of s. -// All other members of s2 are appended to the resulting member list in their original relative order. -// When members are merged, the child SetMatchers are merged by calling this function recursively. +// Merge merges s and s2 and returns a SetMatcher that matches all field paths matched by either s or s2. +// During the merge, members of s and s2 with the same PathElementMatcher merged into a single member +// with the children of each merged by calling this function recursively. func (s *SetMatcher) Merge(s2 *SetMatcher) *SetMatcher { - if s.Wildcard || s2.Wildcard { - return &SetMatcher{Wildcard: true} - } - - // TODO: Optimize. This is O(n^2). In practice, usually a single member is being inserted at a time, - // but that's still O(n). Sorting would help a ton. - var unionedMembers []*SetMemberMatcher - for _, m := range s.Members { - for _, m2 := range s2.Members { - if m.Path.PathElement.Equals(m2.Path.PathElement) && m.Path.Wildcard == m2.Path.Wildcard { - unionedMembers = append(unionedMembers, &SetMemberMatcher{ - Path: m.Path, - Child: m.Child.Merge(m2.Child), - }) - } else { - unionedMembers = append(unionedMembers, m) - } - } - } - for _, m2 := range s2.Members { - for _, existing := range unionedMembers { - if !m2.Path.PathElement.Equals(existing.Path.PathElement) { - unionedMembers = append(unionedMembers, m2) + if s.wildcard || s2.wildcard { + return NewSetMatcher(true) + } + merged := make(sortedMemberMatcher, len(s.members), len(s.members)+len(s2.members)) + copy(merged, s.members) + for _, m := range s2.members { + if i, ok := s.members.Find(m.Path); ok { + // since merged is a shallow copy, do not modify elements in place + merged[i] = &SetMemberMatcher{ + Path: merged[i].Path, + Child: merged[i].Child.Merge(m.Child), } + } else { + merged = append(merged, m) } } - - return &SetMatcher{ - Members: unionedMembers, - } + return NewSetMatcher(false, merged...) // sort happens here } -// SetMemberMatcher defines a pattern that matches the members of a Set. +// SetMemberMatcher defines a matcher that matches the members of a Set. // SetMemberMatcher is structured much like the elements of a SetNodeMap, but // with wildcard support. type SetMemberMatcher struct { - // Path provides a pattern to match members of a Set. + // Path provides a matcher to match members of a Set. // If Path is a wildcard, all members of a Set are included in the match. // Otherwise, if any Path is Equal to a member of a Set, that member is // included in the match and the children of that member are matched - // against the Child pattern. + // against the Child matcher. Path PathElementMatcher - // Child provides a pattern to use for the children of matched members of a Set. + // Child provides a matcher to use for the children of matched members of a Set. Child *SetMatcher } -// PathElementMatcher defined a match pattern for a PathElement. +// PathElementMatcher defined a match matcher for a PathElement. type PathElementMatcher struct { - // Wildcard indicates that all PathElements are matched by this pattern. + // Wildcard indicates that all PathElements are matched by this matcher. // If set, PathElement is ignored. Wildcard bool @@ -274,15 +281,37 @@ type PathElementMatcher struct { PathElement } +func (p PathElementMatcher) Equals(p2 PathElementMatcher) bool { + return p.Wildcard != p2.Wildcard && p.PathElement.Equals(p2.PathElement) +} + +func (p PathElementMatcher) Less(p2 PathElementMatcher) bool { + if p.Wildcard && !p2.Wildcard { + return true + } else if p2.Wildcard { + return false + } + return p.PathElement.Less(p2.PathElement) +} + +func (p PathElementMatcher) Compare(p2 PathElementMatcher) int { + if p.Wildcard && !p2.Wildcard { + return -1 + } else if p2.Wildcard { + return 1 + } + return p.PathElement.Compare(p2.PathElement) +} + // FilterIncludeMatches returns a Set with only the field paths that match. func (s *Set) FilterIncludeMatches(pattern *SetMatcher) *Set { - if pattern.Wildcard { + if pattern.wildcard { return s } members := PathElementSet{} for _, m := range s.Members.members { - for _, pm := range pattern.Members { + for _, pm := range pattern.members { if pm.Path.Wildcard || pm.Path.PathElement.Equals(m) { members.Insert(m) break @@ -635,15 +664,15 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty } } -// FilterIncludeMatches returns a SetNodeMap with only the field paths that match the pattern. +// FilterIncludeMatches returns a SetNodeMap with only the field paths that match the matcher. func (s *SetNodeMap) FilterIncludeMatches(pattern *SetMatcher) *SetNodeMap { - if pattern.Wildcard { + if pattern.wildcard { return s } var out sortedSetNode for _, member := range s.members { - for _, c := range pattern.Members { + for _, c := range pattern.members { if c.Path.Wildcard || c.Path.PathElement.Equals(member.pathElement) { childSet := member.set.FilterIncludeMatches(c.Child) if childSet.Size() > 0 { @@ -694,7 +723,7 @@ func (s *SetNodeMap) Leaves() *SetNodeMap { // NewExcludeSetFilter can be used to create a filter that removes // specific field paths and all of their children. // NewIncludeMatcherFilter can be used to create a filter that removes all fields except -// the fields that match a field path pattern. PrefixMatcher and MakePrefixMatcherOrDie +// the fields that match a field path matcher. PrefixMatcher and MakePrefixMatcherOrDie // can be used to define field path patterns. type Filter interface { // Filter returns a filtered copy of the set. @@ -723,24 +752,25 @@ func (t excludeFilter) Filter(set *Set) *Set { return set.RecursiveDifference(t.excludeSet) } -// NewIncludeMatcherFilter returns a filter that only includes field paths that match the pattern. -// PrefixMatcher and MakePrefixMatcherOrDie can help create basic SetPatterns. -func NewIncludeMatcherFilter(patterns ...*SetMatcher) Filter { - if len(patterns) == 0 { - return includeMatcherFilter{&SetMatcher{Wildcard: true}} +// NewIncludeMatcherFilter returns a filter that only includes field paths that match. +// If no matchers are provided, the filter includes all field paths. +// PrefixMatcher and MakePrefixMatcherOrDie can help create basic matcher. +func NewIncludeMatcherFilter(matchers ...*SetMatcher) Filter { + if len(matchers) == 0 { + return includeMatcherFilter{&SetMatcher{wildcard: true}} } - pattern := patterns[0] - for i := 1; i < len(patterns); i++ { - pattern = pattern.Merge(patterns[i]) + matcher := matchers[0] + for i := 1; i < len(matchers); i++ { + matcher = matcher.Merge(matchers[i]) } - return includeMatcherFilter{pattern} + return includeMatcherFilter{matcher} } type includeMatcherFilter struct { - pattern *SetMatcher + matcher *SetMatcher } func (pf includeMatcherFilter) Filter(set *Set) *Set { - return set.FilterIncludeMatches(pf.pattern) + return set.FilterIncludeMatches(pf.matcher) } diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index 8122dd28..ef6aff13 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -988,8 +988,8 @@ func TestFilterByPattern(t *testing.T) { MakePathOrDie("spec", "list", 2, "f2"), ), filter: NewIncludeMatcherFilter( - MakePrefixMatcherOrDie("spec", "list", MatchAnyPathElement(), "f1"), // wildcard matches first and runs - MakePrefixMatcherOrDie("spec", "list", 1, "f2"), // already matched by wildcard so this is ignored + MakePrefixMatcherOrDie("spec", "list", MatchAnyPathElement(), "f1"), // takes precedence + MakePrefixMatcherOrDie("spec", "list", 1, "f2"), // ignored ), expect: NewSet( MakePathOrDie("spec", "list", 0, "f1"), From bda634ef20dbc5f02fd6acb73919d70bf091b642 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Thu, 31 Oct 2024 13:40:44 -0400 Subject: [PATCH 073/105] Apply feedback --- fieldpath/set.go | 10 ++++-- fieldpath/set_test.go | 78 ++++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/fieldpath/set.go b/fieldpath/set.go index e07d0f3a..77ae2511 100644 --- a/fieldpath/set.go +++ b/fieldpath/set.go @@ -153,7 +153,7 @@ func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher { // // - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well. // - PathElement - for any path element -// - value.FieldList - for associative list keys +// - value.FieldList - for listMap keys // - value.Value - for scalar list elements // - string - For field names // - int - for array indices @@ -164,19 +164,25 @@ func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) { var pattern PathElementMatcher switch t := part.(type) { case PathElementMatcher: + // any path matcher, including wildcard pattern = t case PathElement: + // any path element pattern = PathElementMatcher{PathElement: t} case *value.FieldList: + // a listMap key if len(*t) == 0 { return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)") } pattern = PathElementMatcher{PathElement: PathElement{Key: t}} case value.Value: + // a scalar or set-type list element pattern = PathElementMatcher{PathElement: PathElement{Value: &t}} case string: + // a plain field name pattern = PathElementMatcher{PathElement: PathElement{FieldName: &t}} case int: + // a plain list index pattern = PathElementMatcher{PathElement: PathElement{Index: &t}} default: return nil, fmt.Errorf("unexpected type %T", t) @@ -270,7 +276,7 @@ type SetMemberMatcher struct { Child *SetMatcher } -// PathElementMatcher defined a match matcher for a PathElement. +// PathElementMatcher defined a path matcher for a PathElement. type PathElementMatcher struct { // Wildcard indicates that all PathElements are matched by this matcher. // If set, PathElement is ignored. diff --git a/fieldpath/set_test.go b/fieldpath/set_test.go index ef6aff13..7efb374e 100644 --- a/fieldpath/set_test.go +++ b/fieldpath/set_test.go @@ -769,16 +769,20 @@ func TestFilterByPattern(t *testing.T) { input: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0), MakePathOrDie("spec", "containers", 0, "resources"), MakePathOrDie("spec", "containers", 0, "resources", "limits"), MakePathOrDie("spec", "containers", 0, "resources", "limits", "cpu"), MakePathOrDie("spec", "containers", 0, "resources", "requests"), MakePathOrDie("spec", "containers", 0, "resources", "requests", "cpu"), MakePathOrDie("spec", "containers", 0, "resources", "claims"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0), MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "name"), MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "request"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1), MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "name"), MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "request"), + MakePathOrDie("spec", "containers", 1), MakePathOrDie("spec", "containers", 1, "resources"), MakePathOrDie("spec", "containers", 1, "resources", "limits"), MakePathOrDie("spec", "containers", 1, "resources", "limits", "cpu"), @@ -787,16 +791,20 @@ func TestFilterByPattern(t *testing.T) { expect: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0), MakePathOrDie("spec", "containers", 0, "resources"), MakePathOrDie("spec", "containers", 0, "resources", "limits"), MakePathOrDie("spec", "containers", 0, "resources", "limits", "cpu"), MakePathOrDie("spec", "containers", 0, "resources", "requests"), MakePathOrDie("spec", "containers", 0, "resources", "requests", "cpu"), MakePathOrDie("spec", "containers", 0, "resources", "claims"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 0), MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "name"), MakePathOrDie("spec", "containers", 0, "resources", "claims", 0, "request"), + MakePathOrDie("spec", "containers", 0, "resources", "claims", 1), MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "name"), MakePathOrDie("spec", "containers", 0, "resources", "claims", 1, "request"), + MakePathOrDie("spec", "containers", 1), MakePathOrDie("spec", "containers", 1, "resources"), MakePathOrDie("spec", "containers", 1, "resources", "limits"), MakePathOrDie("spec", "containers", 1, "resources", "limits", "cpu"), @@ -831,6 +839,7 @@ func TestFilterByPattern(t *testing.T) { input: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0), MakePathOrDie("spec", "containers", 0, "image"), MakePathOrDie("spec", "containers", 0, "workingDir"), MakePathOrDie("spec", "containers", 0, "resources"), @@ -839,6 +848,7 @@ func TestFilterByPattern(t *testing.T) { expect: NewSet( MakePathOrDie("spec"), MakePathOrDie("spec", "containers"), + MakePathOrDie("spec", "containers", 0), MakePathOrDie("spec", "containers", 0, "resources"), ), }, @@ -880,44 +890,6 @@ func TestFilterByPattern(t *testing.T) { }, "field"), ), }, - { - name: "filter listMap key", - input: NewSet( - MakePathOrDie("spec"), - MakePathOrDie("spec", "listMap", - &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("value1")}, - {Name: "key2", Value: value.NewValueInterface("value2")}, - }), - MakePathOrDie("spec", "listMap", - &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("value1")}, - {Name: "key2", Value: value.NewValueInterface("value2")}, - }, "field"), - MakePathOrDie("spec", "listMap", - &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("valueX")}, - {Name: "key2", Value: value.NewValueInterface("valueY")}, - }, "field"), - ), - filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "listMap", &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("value1")}, - {Name: "key2", Value: value.NewValueInterface("value2")}, - })), - expect: NewSet( - MakePathOrDie("spec"), - MakePathOrDie("spec", "listMap", - &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("value1")}, - {Name: "key2", Value: value.NewValueInterface("value2")}, - }), - MakePathOrDie("spec", "listMap", - &value.FieldList{ - {Name: "key1", Value: value.NewValueInterface("value1")}, - {Name: "key2", Value: value.NewValueInterface("value2")}, - }, "field"), - ), - }, { name: "filter value", input: NewSet( @@ -935,12 +907,17 @@ func TestFilterByPattern(t *testing.T) { name: "filter by index", input: NewSet( MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 0), MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "value"), ), filter: NewIncludeMatcherFilter(MakePrefixMatcherOrDie("spec", "list", 1, "value")), expect: NewSet( MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "value"), ), }, @@ -948,8 +925,13 @@ func TestFilterByPattern(t *testing.T) { name: "multiple index matchers", input: NewSet( MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 0), MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "value"), + MakePathOrDie("spec", "list", 2), + MakePathOrDie("spec", "list", 2, "value"), ), filter: NewIncludeMatcherFilter( MakePrefixMatcherOrDie("spec", "list", 0, "value"), @@ -957,15 +939,22 @@ func TestFilterByPattern(t *testing.T) { ), expect: NewSet( MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 0), MakePathOrDie("spec", "list", 0, "value"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "value"), ), }, { name: "multiple field matchers", input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "f1"), MakePathOrDie("spec", "f1", "f11"), + MakePathOrDie("spec", "f2"), MakePathOrDie("spec", "f2", "f21"), + MakePathOrDie("spec", "f3"), MakePathOrDie("spec", "f3", "f31"), ), filter: NewIncludeMatcherFilter( @@ -973,17 +962,25 @@ func TestFilterByPattern(t *testing.T) { MakePrefixMatcherOrDie("spec", "f3"), ), expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "f1"), MakePathOrDie("spec", "f1", "f11"), + MakePathOrDie("spec", "f3"), MakePathOrDie("spec", "f3", "f31"), ), }, { name: "wildcard takes precedence", input: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 0), MakePathOrDie("spec", "list", 0, "f1"), MakePathOrDie("spec", "list", 0, "f2"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "f1"), MakePathOrDie("spec", "list", 1, "f2"), + MakePathOrDie("spec", "list", 2), MakePathOrDie("spec", "list", 2, "f1"), MakePathOrDie("spec", "list", 2, "f2"), ), @@ -992,8 +989,13 @@ func TestFilterByPattern(t *testing.T) { MakePrefixMatcherOrDie("spec", "list", 1, "f2"), // ignored ), expect: NewSet( + MakePathOrDie("spec"), + MakePathOrDie("spec", "list"), + MakePathOrDie("spec", "list", 0), MakePathOrDie("spec", "list", 0, "f1"), + MakePathOrDie("spec", "list", 1), MakePathOrDie("spec", "list", 1, "f1"), + MakePathOrDie("spec", "list", 2), MakePathOrDie("spec", "list", 2, "f1"), ), }, From 39c90b668e3ec83e3c343a7e1bee0459ef478135 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Thu, 31 Oct 2024 15:58:22 -0400 Subject: [PATCH 074/105] Add volunteer reviewers --- OWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OWNERS b/OWNERS index 71704c73..12206041 100644 --- a/OWNERS +++ b/OWNERS @@ -8,4 +8,7 @@ emeritus_approvers: - jennybuckley # 2021-05-13 reviewers: - apelisse + - jefftree + - yliaog + - cici37 From adaddb2938e02cd812c7046bfdadf1bc526e232e Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Thu, 14 Nov 2024 01:05:38 -0500 Subject: [PATCH 075/105] Add back IgnoredFields --- internal/fixture/state.go | 6 ++ merge/ignore_test.go | 127 +++++++++++++++++++++++++++++++++++++- merge/update.go | 50 +++++++++++++-- 3 files changed, 177 insertions(+), 6 deletions(-) diff --git a/internal/fixture/state.go b/internal/fixture/state.go index 039125b2..98d9a77f 100644 --- a/internal/fixture/state.go +++ b/internal/fixture/state.go @@ -540,6 +540,10 @@ type TestCase struct { ReturnInputOnNoop bool // IgnoreFilter filters out ignored fields from a fieldpath.Set. IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter + + // IgnoredFields containing the set to ignore for every version. + // IgnoredFields may not be set if IgnoreFilter is set. + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set } // Test runs the test-case using the given parser and a dummy converter. @@ -573,6 +577,7 @@ func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) updaterBuilder := merge.UpdaterBuilder{ Converter: converter, IgnoreFilter: tc.IgnoreFilter, + IgnoredFields: tc.IgnoredFields, ReturnInputOnNoop: tc.ReturnInputOnNoop, } state := State{ @@ -595,6 +600,7 @@ func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) e updaterBuilder := merge.UpdaterBuilder{ Converter: converter, IgnoreFilter: tc.IgnoreFilter, + IgnoredFields: tc.IgnoredFields, ReturnInputOnNoop: tc.ReturnInputOnNoop, } state := State{ diff --git a/merge/ignore_test.go b/merge/ignore_test.go index 5a8b4200..07fdbce0 100644 --- a/merge/ignore_test.go +++ b/merge/ignore_test.go @@ -23,7 +23,7 @@ import ( . "sigs.k8s.io/structured-merge-diff/v4/internal/fixture" ) -func TestIgnoredFields(t *testing.T) { +func TestIgnoreFilter(t *testing.T) { tests := map[string]TestCase{ "update_does_not_own_ignored": { APIVersion: "v1", @@ -148,6 +148,131 @@ func TestIgnoredFields(t *testing.T) { } } +func TestIgnoredFields(t *testing.T) { + tests := map[string]TestCase{ + "update_does_not_own_ignored": { + APIVersion: "v1", + Ops: []Operation{ + Update{ + Manager: "default", + APIVersion: "v1", + Object: ` + numeric: 1 + string: "some string" + `, + }, + }, + Object: ` + numeric: 1 + string: "some string" + `, + Managed: fieldpath.ManagedFields{ + "default": fieldpath.NewVersionedSet( + _NS( + _P("numeric"), + ), + "v1", + false, + ), + }, + IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ + "v1": _NS( + _P("string"), + ), + }, + }, + "update_does_not_own_deep_ignored": { + APIVersion: "v1", + Ops: []Operation{ + Update{ + Manager: "default", + APIVersion: "v1", + Object: `{"numeric": 1, "obj": {"string": "foo", "numeric": 2}}`, + }, + }, + Object: `{"numeric": 1, "obj": {"string": "foo", "numeric": 2}}`, + Managed: fieldpath.ManagedFields{ + "default": fieldpath.NewVersionedSet( + _NS( + _P("numeric"), + ), + "v1", + false, + ), + }, + IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ + "v1": _NS( + _P("obj"), + ), + }, + }, + "apply_does_not_own_ignored": { + APIVersion: "v1", + Ops: []Operation{ + Apply{ + Manager: "default", + APIVersion: "v1", + Object: ` + numeric: 1 + string: "some string" + `, + }, + }, + Object: ` + numeric: 1 + string: "some string" + `, + Managed: fieldpath.ManagedFields{ + "default": fieldpath.NewVersionedSet( + _NS( + _P("numeric"), + ), + "v1", + true, + ), + }, + IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ + "v1": _NS( + _P("string"), + ), + }, + }, + "apply_does_not_own_deep_ignored": { + APIVersion: "v1", + Ops: []Operation{ + Apply{ + Manager: "default", + APIVersion: "v1", + Object: `{"numeric": 1, "obj": {"string": "foo", "numeric": 2}}`, + }, + }, + Object: `{"numeric": 1, "obj": {"string": "foo", "numeric": 2}}`, + Managed: fieldpath.ManagedFields{ + "default": fieldpath.NewVersionedSet( + _NS( + _P("numeric"), + ), + "v1", + true, + ), + }, + IgnoredFields: map[fieldpath.APIVersion]*fieldpath.Set{ + "v1": _NS( + _P("obj"), + ), + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if err := test.Test(DeducedParser); err != nil { + t.Fatal("Should fail:", err) + } + }) + } +} + func TestFilteredFieldsUsesVersions(t *testing.T) { tests := map[string]TestCase{ "does_use_ignored_fields_versions": { diff --git a/merge/update.go b/merge/update.go index 34ab2d6f..455818ff 100644 --- a/merge/update.go +++ b/merge/update.go @@ -33,6 +33,9 @@ type UpdaterBuilder struct { Converter Converter IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter + // IgnoredFields provides a set of fields to ignore for each + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Stop comparing the new object with old object after applying. // This was initially used to avoid spurious etcd update, but // since that's vastly inefficient, we've come-up with a better @@ -46,6 +49,7 @@ func (u *UpdaterBuilder) BuildUpdater() *Updater { return &Updater{ Converter: u.Converter, IgnoreFilter: u.IgnoreFilter, + IgnoredFields: u.IgnoredFields, returnInputOnNoop: u.ReturnInputOnNoop, } } @@ -56,6 +60,9 @@ type Updater struct { // Deprecated: This will eventually become private. Converter Converter + // Deprecated: This will eventually become private. + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Deprecated: This will eventually become private. IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter @@ -70,8 +77,19 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions := map[fieldpath.APIVersion]*typed.Comparison{ - version: compare.FilterFields(s.IgnoreFilter[version]), + var versions map[fieldpath.APIVersion]*typed.Comparison + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + if s.IgnoredFields != nil { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.ExcludeFields(s.IgnoredFields[version]), + } + } else { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.FilterFields(s.IgnoreFilter[version]), + } } for manager, managerSet := range managers { @@ -101,7 +119,12 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa if err != nil { return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + + if s.IgnoredFields != nil { + versions[managerSet.APIVersion()] = compare.ExcludeFields(s.IgnoredFields[managerSet.APIVersion()]) + } else { + versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + } } conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added)) @@ -154,7 +177,16 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false) } set := managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added) - ignoreFilter := s.IgnoreFilter[version] + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) } @@ -189,7 +221,15 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err) } - ignoreFilter := s.IgnoreFilter[version] + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) } From bfb8f51273da947bd6adf1bd3d56675fa1fc02ed Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Fri, 15 Nov 2024 20:10:34 -0500 Subject: [PATCH 076/105] Run go mod vendor --- vendor/github.com/google/go-cmp/LICENSE | 27 + .../github.com/google/go-cmp/cmp/compare.go | 669 ++++++++++++++++++ .../google/go-cmp/cmp/export_panic.go | 16 + .../google/go-cmp/cmp/export_unsafe.go | 36 + .../go-cmp/cmp/internal/diff/debug_disable.go | 18 + .../go-cmp/cmp/internal/diff/debug_enable.go | 123 ++++ .../google/go-cmp/cmp/internal/diff/diff.go | 402 +++++++++++ .../google/go-cmp/cmp/internal/flags/flags.go | 9 + .../go-cmp/cmp/internal/function/func.go | 99 +++ .../google/go-cmp/cmp/internal/value/name.go | 164 +++++ .../cmp/internal/value/pointer_purego.go | 34 + .../cmp/internal/value/pointer_unsafe.go | 37 + .../google/go-cmp/cmp/internal/value/sort.go | 106 +++ .../github.com/google/go-cmp/cmp/options.go | 554 +++++++++++++++ vendor/github.com/google/go-cmp/cmp/path.go | 380 ++++++++++ vendor/github.com/google/go-cmp/cmp/report.go | 54 ++ .../google/go-cmp/cmp/report_compare.go | 433 ++++++++++++ .../google/go-cmp/cmp/report_references.go | 264 +++++++ .../google/go-cmp/cmp/report_reflect.go | 414 +++++++++++ .../google/go-cmp/cmp/report_slices.go | 614 ++++++++++++++++ .../google/go-cmp/cmp/report_text.go | 432 +++++++++++ .../google/go-cmp/cmp/report_value.go | 121 ++++ vendor/github.com/json-iterator/go/README.md | 38 +- vendor/github.com/json-iterator/go/adapter.go | 2 +- vendor/github.com/json-iterator/go/any_str.go | 4 +- vendor/github.com/json-iterator/go/config.go | 4 +- vendor/github.com/json-iterator/go/go.mod | 11 + vendor/github.com/json-iterator/go/go.sum | 14 + vendor/github.com/json-iterator/go/iter.go | 27 + .../github.com/json-iterator/go/iter_array.go | 10 +- .../github.com/json-iterator/go/iter_float.go | 3 + .../github.com/json-iterator/go/iter_int.go | 3 +- .../json-iterator/go/iter_object.go | 28 +- .../github.com/json-iterator/go/iter_skip.go | 25 +- .../json-iterator/go/iter_skip_sloppy.go | 19 + vendor/github.com/json-iterator/go/reflect.go | 7 +- .../json-iterator/go/reflect_extension.go | 6 +- .../go/reflect_json_raw_message.go | 24 +- .../json-iterator/go/reflect_map.go | 94 +-- .../json-iterator/go/reflect_marshaler.go | 12 +- .../json-iterator/go/reflect_native.go | 14 +- .../json-iterator/go/reflect_optional.go | 4 - .../go/reflect_struct_decoder.go | 73 +- .../go/reflect_struct_encoder.go | 1 + vendor/github.com/json-iterator/go/stream.go | 5 +- .../json-iterator/go/stream_float.go | 17 + .../github.com/modern-go/reflect2/.travis.yml | 2 +- .../github.com/modern-go/reflect2/Gopkg.lock | 8 +- .../github.com/modern-go/reflect2/Gopkg.toml | 4 - vendor/github.com/modern-go/reflect2/go.mod | 3 + .../modern-go/reflect2/go_above_118.go | 23 + .../modern-go/reflect2/go_above_17.go | 8 - .../modern-go/reflect2/go_above_19.go | 3 + .../modern-go/reflect2/go_below_118.go | 21 + .../modern-go/reflect2/go_below_17.go | 9 - .../modern-go/reflect2/go_below_19.go | 14 - .../github.com/modern-go/reflect2/reflect2.go | 20 +- vendor/github.com/modern-go/reflect2/test.sh | 12 - .../github.com/modern-go/reflect2/type_map.go | 51 +- .../modern-go/reflect2/unsafe_link.go | 26 +- .../modern-go/reflect2/unsafe_map.go | 8 - vendor/gopkg.in/yaml.v2/.travis.yml | 16 - vendor/gopkg.in/yaml.v2/go.mod | 5 - vendor/modules.txt | 14 +- vendor/sigs.k8s.io/yaml/LICENSE | 306 ++++++++ .../yaml/goyaml.v2}/LICENSE | 0 .../yaml/goyaml.v2}/LICENSE.libyaml | 0 .../yaml/goyaml.v2}/NOTICE | 0 vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS | 24 + .../yaml/goyaml.v2}/README.md | 10 + .../yaml/goyaml.v2}/apic.go | 5 + .../yaml/goyaml.v2}/decode.go | 0 .../yaml/goyaml.v2}/emitterc.go | 0 .../yaml/goyaml.v2}/encode.go | 0 .../yaml/goyaml.v2}/parserc.go | 0 .../yaml/goyaml.v2}/readerc.go | 0 .../yaml/goyaml.v2}/resolve.go | 0 .../yaml/goyaml.v2}/scannerc.go | 0 .../yaml/goyaml.v2}/sorter.go | 0 .../yaml/goyaml.v2}/writerc.go | 0 .../yaml/goyaml.v2}/yaml.go | 14 +- .../yaml/goyaml.v2}/yamlh.go | 0 .../yaml/goyaml.v2}/yamlprivateh.go | 0 83 files changed, 5778 insertions(+), 279 deletions(-) create mode 100644 vendor/github.com/google/go-cmp/LICENSE create mode 100644 vendor/github.com/google/go-cmp/cmp/compare.go create mode 100644 vendor/github.com/google/go-cmp/cmp/export_panic.go create mode 100644 vendor/github.com/google/go-cmp/cmp/export_unsafe.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/function/func.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/name.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/sort.go create mode 100644 vendor/github.com/google/go-cmp/cmp/options.go create mode 100644 vendor/github.com/google/go-cmp/cmp/path.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_compare.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_references.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_reflect.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_slices.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_text.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_value.go create mode 100644 vendor/github.com/json-iterator/go/go.mod create mode 100644 vendor/github.com/json-iterator/go/go.sum create mode 100644 vendor/github.com/modern-go/reflect2/go.mod create mode 100644 vendor/github.com/modern-go/reflect2/go_above_118.go delete mode 100644 vendor/github.com/modern-go/reflect2/go_above_17.go create mode 100644 vendor/github.com/modern-go/reflect2/go_below_118.go delete mode 100644 vendor/github.com/modern-go/reflect2/go_below_17.go delete mode 100644 vendor/github.com/modern-go/reflect2/go_below_19.go delete mode 100644 vendor/github.com/modern-go/reflect2/test.sh delete mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml delete mode 100644 vendor/gopkg.in/yaml.v2/go.mod create mode 100644 vendor/sigs.k8s.io/yaml/LICENSE rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/LICENSE (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/LICENSE.libyaml (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/NOTICE (100%) create mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/README.md (84%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/apic.go (99%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/decode.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/emitterc.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/encode.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/parserc.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/readerc.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/resolve.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/scannerc.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/sorter.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/writerc.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/yaml.go (95%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/yamlh.go (100%) rename vendor/{gopkg.in/yaml.v2 => sigs.k8s.io/yaml/goyaml.v2}/yamlprivateh.go (100%) diff --git a/vendor/github.com/google/go-cmp/LICENSE b/vendor/github.com/google/go-cmp/LICENSE new file mode 100644 index 00000000..32017f8f --- /dev/null +++ b/vendor/github.com/google/go-cmp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go new file mode 100644 index 00000000..087320da --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -0,0 +1,669 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmp determines equality of values. +// +// This package is intended to be a more powerful and safer alternative to +// reflect.DeepEqual for comparing whether two values are semantically equal. +// It is intended to only be used in tests, as performance is not a goal and +// it may panic if it cannot compare the values. Its propensity towards +// panicking means that its unsuitable for production environments where a +// spurious panic may be fatal. +// +// The primary features of cmp are: +// +// - When the default behavior of equality does not suit the test's needs, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as +// they are within some tolerance of each other. +// +// - Types with an Equal method may use that method to determine equality. +// This allows package authors to determine the equality operation +// for the types that they define. +// +// - If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on +// both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, +// unexported fields are not compared by default; they result in panics +// unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported) +// or explicitly compared using the Exporter option. +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/function" + "github.com/google/go-cmp/cmp/internal/value" +) + +// TODO(≥go1.18): Use any instead of interface{}. + +// Equal reports whether x and y are equal by recursively applying the +// following rules in the given order to x and y and all of their sub-values: +// +// - Let S be the set of all Ignore, Transformer, and Comparer options that +// remain after applying all path filters, value filters, and type filters. +// If at least one Ignore exists in S, then the comparison is ignored. +// If the number of Transformer and Comparer options in S is non-zero, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single Transformer, then use that to transform +// the current values and recursively call Equal on the output values. +// If S contains a single Comparer, then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. +// +// - If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and +// evaluation proceeds to the next rule. +// +// - Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, +// and channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. +// +// Structs are equal if recursively calling Equal on all fields report equal. +// If a struct contains unexported fields, Equal panics unless an Ignore option +// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option +// explicitly permits comparing the unexported field. +// +// Slices are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored slice or array elements report equal. +// Empty non-nil slices and nil slices are not equal; to equate empty slices, +// consider using cmpopts.EquateEmpty. +// +// Maps are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored map entries report equal. +// Map keys are equal according to the == operator. +// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// Empty non-nil maps and nil maps are not equal; to equate empty maps, +// consider using cmpopts.EquateEmpty. +// +// Pointers and interfaces are equal if they are both nil or both non-nil, +// where they have the same underlying concrete type and recursively +// calling Equal on the underlying values reports equal. +// +// Before recursing into a pointer, slice element, or map, the current path +// is checked to detect whether the address has already been visited. +// If there is a cycle, then the pointed at values are considered equal +// only if both addresses were previously visited in the same path step. +func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(rootStep(x, y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values: +// y - x. It returns an empty string if and only if Equal returns true for the +// same input values and options. +// +// The output is displayed as a literal in pseudo-Go syntax. +// At the start of each line, a "-" prefix indicates an element removed from x, +// a "+" prefix to indicates an element added from y, and the lack of a prefix +// indicates an element common to both x and y. If possible, the output +// uses fmt.Stringer.String or error.Error methods to produce more humanly +// readable outputs. In such cases, the string is prefixed with either an +// 's' or 'e' character, respectively, to indicate that the method was called. +// +// Do not depend on this output being stable. If you need the ability to +// programmatically interpret the difference, consider using a custom Reporter. +func Diff(x, y interface{}, opts ...Option) string { + s := newState(opts) + + // Optimization: If there are no other reporters, we can optimize for the + // common case where the result is equal (and thus no reported difference). + // This avoids the expensive construction of a difference tree. + if len(s.reporters) == 0 { + s.compareAny(rootStep(x, y)) + if s.result.Equal() { + return "" + } + s.result = diff.Result{} // Reset results + } + + r := new(defaultReporter) + s.reporters = append(s.reporters, reporter{r}) + s.compareAny(rootStep(x, y)) + d := r.String() + if (d == "") != s.result.Equal() { + panic("inconsistent difference and equality results") + } + return d +} + +// rootStep constructs the first path step. If x and y have differing types, +// then they are stored within an empty interface type. +func rootStep(x, y interface{}) PathStep { + vx := reflect.ValueOf(x) + vy := reflect.ValueOf(y) + + // If the inputs are different types, auto-wrap them in an empty interface + // so that they have the same parent type. + var t reflect.Type + if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { + t = anyType + if vx.IsValid() { + vvx := reflect.New(t).Elem() + vvx.Set(vx) + vx = vvx + } + if vy.IsValid() { + vvy := reflect.New(t).Elem() + vvy.Set(vy) + vy = vvy + } + } else { + t = vx.Type() + } + + return &pathStep{t, vx, vy} +} + +type state struct { + // These fields represent the "comparison state". + // Calling statelessCompare must not result in observable changes to these. + result diff.Result // The current result of comparison + curPath Path // The current path in the value tree + curPtrs pointerPath // The current set of visited pointers + reporters []reporter // Optional reporters + + // recChecker checks for infinite cycles applying the same set of + // transformers upon the output of itself. + recChecker recChecker + + // dynChecker triggers pseudo-random checks for option correctness. + // It is safe for statelessCompare to mutate this value. + dynChecker dynChecker + + // These fields, once set by processOption, will not change. + exporters []exporter // List of exporters for structs with unexported fields + opts Options // List of all fundamental and filter options +} + +func newState(opts []Option) *state { + // Always ensure a validator option exists to validate the inputs. + s := &state{opts: Options{validator{}}} + s.curPtrs.Init() + s.processOption(Options(opts)) + return s +} + +func (s *state) processOption(opt Option) { + switch opt := opt.(type) { + case nil: + case Options: + for _, o := range opt { + s.processOption(o) + } + case coreOption: + type filtered interface { + isFiltered() bool + } + if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { + panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) + } + s.opts = append(s.opts, opt) + case exporter: + s.exporters = append(s.exporters, opt) + case reporter: + s.reporters = append(s.reporters, opt) + default: + panic(fmt.Sprintf("unknown option %T", opt)) + } +} + +// statelessCompare compares two values and returns the result. +// This function is stateless in that it does not alter the current result, +// or output to any registered reporters. +func (s *state) statelessCompare(step PathStep) diff.Result { + // We do not save and restore curPath and curPtrs because all of the + // compareX methods should properly push and pop from them. + // It is an implementation bug if the contents of the paths differ from + // when calling this function to when returning from it. + + oldResult, oldReporters := s.result, s.reporters + s.result = diff.Result{} // Reset result + s.reporters = nil // Remove reporters to avoid spurious printouts + s.compareAny(step) + res := s.result + s.result, s.reporters = oldResult, oldReporters + return res +} + +func (s *state) compareAny(step PathStep) { + // Update the path stack. + s.curPath.push(step) + defer s.curPath.pop() + for _, r := range s.reporters { + r.PushStep(step) + defer r.PopStep() + } + s.recChecker.Check(s.curPath) + + // Cycle-detection for slice elements (see NOTE in compareSlice). + t := step.Type() + vx, vy := step.Values() + if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { + px, py := vx.Addr(), vy.Addr() + if eq, visited := s.curPtrs.Push(px, py); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(px, py) + } + + // Rule 1: Check whether an option applies on this node in the value tree. + if s.tryOptions(t, vx, vy) { + return + } + + // Rule 2: Check whether the type has a valid Equal method. + if s.tryMethod(t, vx, vy) { + return + } + + // Rule 3: Compare based on the underlying kind. + switch t.Kind() { + case reflect.Bool: + s.report(vx.Bool() == vy.Bool(), 0) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s.report(vx.Int() == vy.Int(), 0) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s.report(vx.Uint() == vy.Uint(), 0) + case reflect.Float32, reflect.Float64: + s.report(vx.Float() == vy.Float(), 0) + case reflect.Complex64, reflect.Complex128: + s.report(vx.Complex() == vy.Complex(), 0) + case reflect.String: + s.report(vx.String() == vy.String(), 0) + case reflect.Chan, reflect.UnsafePointer: + s.report(vx.Pointer() == vy.Pointer(), 0) + case reflect.Func: + s.report(vx.IsNil() && vy.IsNil(), 0) + case reflect.Struct: + s.compareStruct(t, vx, vy) + case reflect.Slice, reflect.Array: + s.compareSlice(t, vx, vy) + case reflect.Map: + s.compareMap(t, vx, vy) + case reflect.Ptr: + s.comparePtr(t, vx, vy) + case reflect.Interface: + s.compareInterface(t, vx, vy) + default: + panic(fmt.Sprintf("%v kind not handled", t.Kind())) + } +} + +func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { + // Evaluate all filters and apply the remaining options. + if opt := s.opts.filter(s, t, vx, vy); opt != nil { + opt.apply(s, vx, vy) + return true + } + return false +} + +func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { + // Check if this type even has an Equal method. + m, ok := t.MethodByName("Equal") + if !ok || !function.IsType(m.Type, function.EqualAssignable) { + return false + } + + eq := s.callTTBFunc(m.Func, vx, vy) + s.report(eq, reportByMethod) + return true +} + +func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{v})[0] + } + + // Run the function twice and ensure that we get the same results back. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, v) + got := <-c + want := f.Call([]reflect.Value{v})[0] + if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { + // To avoid false-positives with non-reflexive equality operations, + // we sanity check whether a value is equal to itself. + if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { + return want + } + panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) + } + return want +} + +func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{x, y})[0].Bool() + } + + // Swapping the input arguments is sufficient to check that + // f is symmetric and deterministic. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, y, x) + got := <-c + want := f.Call([]reflect.Value{x, y})[0].Bool() + if !got.IsValid() || got.Bool() != want { + panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) + } + return want +} + +func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { + var ret reflect.Value + defer func() { + recover() // Ignore panics, let the other call to f panic instead + c <- ret + }() + ret = f.Call(vs)[0] +} + +func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { + var addr bool + var vax, vay reflect.Value // Addressable versions of vx and vy + + var mayForce, mayForceInit bool + step := StructField{&structField{}} + for i := 0; i < t.NumField(); i++ { + step.typ = t.Field(i).Type + step.vx = vx.Field(i) + step.vy = vy.Field(i) + step.name = t.Field(i).Name + step.idx = i + step.unexported = !isExported(step.name) + if step.unexported { + if step.name == "_" { + continue + } + // Defer checking of unexported fields until later to give an + // Ignore a chance to ignore the field. + if !vax.IsValid() || !vay.IsValid() { + // For retrieveUnexportedField to work, the parent struct must + // be addressable. Create a new copy of the values if + // necessary to make them addressable. + addr = vx.CanAddr() || vy.CanAddr() + vax = makeAddressable(vx) + vay = makeAddressable(vy) + } + if !mayForceInit { + for _, xf := range s.exporters { + mayForce = mayForce || xf(t) + } + mayForceInit = true + } + step.mayForce = mayForce + step.paddr = addr + step.pvx = vax + step.pvy = vay + step.field = t.Field(i) + } + s.compareAny(step) + } +} + +func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { + isSlice := t.Kind() == reflect.Slice + if isSlice && (vx.IsNil() || vy.IsNil()) { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer + // since slices represents a list of pointers, rather than a single pointer. + // The pointer checking logic must be handled on a per-element basis + // in compareAny. + // + // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting + // pointer P, a length N, and a capacity C. Supposing each slice element has + // a memory size of M, then the slice is equivalent to the list of pointers: + // [P+i*M for i in range(N)] + // + // For example, v[:0] and v[:1] are slices with the same starting pointer, + // but they are clearly different values. Using the slice pointer alone + // violates the assumption that equal pointers implies equal values. + + step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} + withIndexes := func(ix, iy int) SliceIndex { + if ix >= 0 { + step.vx, step.xkey = vx.Index(ix), ix + } else { + step.vx, step.xkey = reflect.Value{}, -1 + } + if iy >= 0 { + step.vy, step.ykey = vy.Index(iy), iy + } else { + step.vy, step.ykey = reflect.Value{}, -1 + } + return step + } + + // Ignore options are able to ignore missing elements in a slice. + // However, detecting these reliably requires an optimal differencing + // algorithm, for which diff.Difference is not. + // + // Instead, we first iterate through both slices to detect which elements + // would be ignored if standing alone. The index of non-discarded elements + // are stored in a separate slice, which diffing is then performed on. + var indexesX, indexesY []int + var ignoredX, ignoredY []bool + for ix := 0; ix < vx.Len(); ix++ { + ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 + if !ignored { + indexesX = append(indexesX, ix) + } + ignoredX = append(ignoredX, ignored) + } + for iy := 0; iy < vy.Len(); iy++ { + ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 + if !ignored { + indexesY = append(indexesY, iy) + } + ignoredY = append(ignoredY, ignored) + } + + // Compute an edit-script for slices vx and vy (excluding ignored elements). + edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { + return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) + }) + + // Replay the ignore-scripts and the edit-script. + var ix, iy int + for ix < vx.Len() || iy < vy.Len() { + var e diff.EditType + switch { + case ix < len(ignoredX) && ignoredX[ix]: + e = diff.UniqueX + case iy < len(ignoredY) && ignoredY[iy]: + e = diff.UniqueY + default: + e, edits = edits[0], edits[1:] + } + switch e { + case diff.UniqueX: + s.compareAny(withIndexes(ix, -1)) + ix++ + case diff.UniqueY: + s.compareAny(withIndexes(-1, iy)) + iy++ + default: + s.compareAny(withIndexes(ix, iy)) + ix++ + iy++ + } + } +} + +func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for maps. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + // We combine and sort the two map keys so that we can perform the + // comparisons in a deterministic order. + step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} + for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { + step.vx = vx.MapIndex(k) + step.vy = vy.MapIndex(k) + step.key = k + if !step.vx.IsValid() && !step.vy.IsValid() { + // It is possible for both vx and vy to be invalid if the + // key contained a NaN value in it. + // + // Even with the ability to retrieve NaN keys in Go 1.12, + // there still isn't a sensible way to compare the values since + // a NaN key may map to multiple unordered values. + // The most reasonable way to compare NaNs would be to compare the + // set of values. However, this is impossible to do efficiently + // since set equality is provably an O(n^2) operation given only + // an Equal function. If we had a Less function or Hash function, + // this could be done in O(n*log(n)) or O(n), respectively. + // + // Rather than adding complex logic to deal with NaNs, make it + // the user's responsibility to compare such obscure maps. + const help = "consider providing a Comparer to compare the map" + panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) + } + s.compareAny(step) + } +} + +func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for pointers. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + vx, vy = vx.Elem(), vy.Elem() + s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) +} + +func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + vx, vy = vx.Elem(), vy.Elem() + if vx.Type() != vy.Type() { + s.report(false, 0) + return + } + s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) +} + +func (s *state) report(eq bool, rf resultFlags) { + if rf&reportByIgnore == 0 { + if eq { + s.result.NumSame++ + rf |= reportEqual + } else { + s.result.NumDiff++ + rf |= reportUnequal + } + } + for _, r := range s.reporters { + r.Report(Result{flags: rf}) + } +} + +// recChecker tracks the state needed to periodically perform checks that +// user provided transformers are not stuck in an infinitely recursive cycle. +type recChecker struct{ next int } + +// Check scans the Path for any recursive transformers and panics when any +// recursive transformers are detected. Note that the presence of a +// recursive Transformer does not necessarily imply an infinite cycle. +// As such, this check only activates after some minimal number of path steps. +func (rc *recChecker) Check(p Path) { + const minLen = 1 << 16 + if rc.next == 0 { + rc.next = minLen + } + if len(p) < rc.next { + return + } + rc.next <<= 1 + + // Check whether the same transformer has appeared at least twice. + var ss []string + m := map[Option]int{} + for _, ps := range p { + if t, ok := ps.(Transform); ok { + t := t.Option() + if m[t] == 1 { // Transformer was used exactly once before + tf := t.(*transformer).fnc.Type() + ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) + } + m[t]++ + } + } + if len(ss) > 0 { + const warning = "recursive set of Transformers detected" + const help = "consider using cmpopts.AcyclicTransformer" + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) + } +} + +// dynChecker tracks the state needed to periodically perform checks that +// user provided functions are symmetric and deterministic. +// The zero value is safe for immediate use. +type dynChecker struct{ curr, next int } + +// Next increments the state and reports whether a check should be performed. +// +// Checks occur every Nth function call, where N is a triangular number: +// +// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... +// +// See https://en.wikipedia.org/wiki/Triangular_number +// +// This sequence ensures that the cost of checks drops significantly as +// the number of functions calls grows larger. +func (dc *dynChecker) Next() bool { + ok := dc.curr == dc.next + if ok { + dc.curr = 0 + dc.next++ + } + dc.curr++ + return ok +} + +// makeAddressable returns a value that is always addressable. +// It returns the input verbatim if it is already addressable, +// otherwise it creates a new value and returns an addressable copy. +func makeAddressable(v reflect.Value) reflect.Value { + if v.CanAddr() { + return v + } + vc := reflect.New(v.Type()).Elem() + vc.Set(v) + return vc +} diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go new file mode 100644 index 00000000..ae851fe5 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/export_panic.go @@ -0,0 +1,16 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build purego +// +build purego + +package cmp + +import "reflect" + +const supportExporters = false + +func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { + panic("no support for forcibly accessing unexported fields") +} diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go new file mode 100644 index 00000000..e2c0f74e --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go @@ -0,0 +1,36 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego +// +build !purego + +package cmp + +import ( + "reflect" + "unsafe" +) + +const supportExporters = true + +// retrieveUnexportedField uses unsafe to forcibly retrieve any field from +// a struct such that the value has read-write permissions. +// +// The parent struct, v, must be addressable, while f must be a StructField +// describing the field to retrieve. If addr is false, +// then the returned value will be shallowed copied to be non-addressable. +func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { + ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() + if !addr { + // A field is addressable if and only if the struct is addressable. + // If the original parent value was not addressable, shallow copy the + // value to make it non-addressable to avoid leaking an implementation + // detail of how forcibly exporting a field works. + if ve.Kind() == reflect.Interface && ve.IsNil() { + return reflect.Zero(f.Type) + } + return reflect.ValueOf(ve.Interface()).Convert(f.Type) + } + return ve +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go new file mode 100644 index 00000000..36062a60 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go @@ -0,0 +1,18 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !cmp_debug +// +build !cmp_debug + +package diff + +var debug debugger + +type debugger struct{} + +func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { + return f +} +func (debugger) Update() {} +func (debugger) Finish() {} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go new file mode 100644 index 00000000..a3b97a1a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go @@ -0,0 +1,123 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cmp_debug +// +build cmp_debug + +package diff + +import ( + "fmt" + "strings" + "sync" + "time" +) + +// The algorithm can be seen running in real-time by enabling debugging: +// go test -tags=cmp_debug -v +// +// Example output: +// === RUN TestDifference/#34 +// ┌───────────────────────────────┐ +// │ \ · · · · · · · · · · · · · · │ +// │ · # · · · · · · · · · · · · · │ +// │ · \ · · · · · · · · · · · · · │ +// │ · · \ · · · · · · · · · · · · │ +// │ · · · X # · · · · · · · · · · │ +// │ · · · # \ · · · · · · · · · · │ +// │ · · · · · # # · · · · · · · · │ +// │ · · · · · # \ · · · · · · · · │ +// │ · · · · · · · \ · · · · · · · │ +// │ · · · · · · · · \ · · · · · · │ +// │ · · · · · · · · · \ · · · · · │ +// │ · · · · · · · · · · \ · · # · │ +// │ · · · · · · · · · · · \ # # · │ +// │ · · · · · · · · · · · # # # · │ +// │ · · · · · · · · · · # # # # · │ +// │ · · · · · · · · · # # # # # · │ +// │ · · · · · · · · · · · · · · \ │ +// └───────────────────────────────┘ +// [.Y..M.XY......YXYXY.|] +// +// The grid represents the edit-graph where the horizontal axis represents +// list X and the vertical axis represents list Y. The start of the two lists +// is the top-left, while the ends are the bottom-right. The '·' represents +// an unexplored node in the graph. The '\' indicates that the two symbols +// from list X and Y are equal. The 'X' indicates that two symbols are similar +// (but not exactly equal) to each other. The '#' indicates that the two symbols +// are different (and not similar). The algorithm traverses this graph trying to +// make the paths starting in the top-left and the bottom-right connect. +// +// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents +// the currently established path from the forward and reverse searches, +// separated by a '|' character. + +const ( + updateDelay = 100 * time.Millisecond + finishDelay = 500 * time.Millisecond + ansiTerminal = true // ANSI escape codes used to move terminal cursor +) + +var debug debugger + +type debugger struct { + sync.Mutex + p1, p2 EditScript + fwdPath, revPath *EditScript + grid []byte + lines int +} + +func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { + dbg.Lock() + dbg.fwdPath, dbg.revPath = p1, p2 + top := "┌─" + strings.Repeat("──", nx) + "┐\n" + row := "│ " + strings.Repeat("· ", nx) + "│\n" + btm := "└─" + strings.Repeat("──", nx) + "┘\n" + dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) + dbg.lines = strings.Count(dbg.String(), "\n") + fmt.Print(dbg) + + // Wrap the EqualFunc so that we can intercept each result. + return func(ix, iy int) (r Result) { + cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] + for i := range cell { + cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot + } + switch r = f(ix, iy); { + case r.Equal(): + cell[0] = '\\' + case r.Similar(): + cell[0] = 'X' + default: + cell[0] = '#' + } + return + } +} + +func (dbg *debugger) Update() { + dbg.print(updateDelay) +} + +func (dbg *debugger) Finish() { + dbg.print(finishDelay) + dbg.Unlock() +} + +func (dbg *debugger) String() string { + dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] + for i := len(*dbg.revPath) - 1; i >= 0; i-- { + dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) + } + return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) +} + +func (dbg *debugger) print(d time.Duration) { + if ansiTerminal { + fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor + } + fmt.Print(dbg) + time.Sleep(d) +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go new file mode 100644 index 00000000..a248e543 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go @@ -0,0 +1,402 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package diff implements an algorithm for producing edit-scripts. +// The edit-script is a sequence of operations needed to transform one list +// of symbols into another (or vice-versa). The edits allowed are insertions, +// deletions, and modifications. The summation of all edits is called the +// Levenshtein distance as this problem is well-known in computer science. +// +// This package prioritizes performance over accuracy. That is, the run time +// is more important than obtaining a minimal Levenshtein distance. +package diff + +import ( + "math/rand" + "time" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +// EditType represents a single operation within an edit-script. +type EditType uint8 + +const ( + // Identity indicates that a symbol pair is identical in both list X and Y. + Identity EditType = iota + // UniqueX indicates that a symbol only exists in X and not Y. + UniqueX + // UniqueY indicates that a symbol only exists in Y and not X. + UniqueY + // Modified indicates that a symbol pair is a modification of each other. + Modified +) + +// EditScript represents the series of differences between two lists. +type EditScript []EditType + +// String returns a human-readable string representing the edit-script where +// Identity, UniqueX, UniqueY, and Modified are represented by the +// '.', 'X', 'Y', and 'M' characters, respectively. +func (es EditScript) String() string { + b := make([]byte, len(es)) + for i, e := range es { + switch e { + case Identity: + b[i] = '.' + case UniqueX: + b[i] = 'X' + case UniqueY: + b[i] = 'Y' + case Modified: + b[i] = 'M' + default: + panic("invalid edit-type") + } + } + return string(b) +} + +// stats returns a histogram of the number of each type of edit operation. +func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { + for _, e := range es { + switch e { + case Identity: + s.NI++ + case UniqueX: + s.NX++ + case UniqueY: + s.NY++ + case Modified: + s.NM++ + default: + panic("invalid edit-type") + } + } + return +} + +// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if +// lists X and Y are equal. +func (es EditScript) Dist() int { return len(es) - es.stats().NI } + +// LenX is the length of the X list. +func (es EditScript) LenX() int { return len(es) - es.stats().NY } + +// LenY is the length of the Y list. +func (es EditScript) LenY() int { return len(es) - es.stats().NX } + +// EqualFunc reports whether the symbols at indexes ix and iy are equal. +// When called by Difference, the index is guaranteed to be within nx and ny. +type EqualFunc func(ix int, iy int) Result + +// Result is the result of comparison. +// NumSame is the number of sub-elements that are equal. +// NumDiff is the number of sub-elements that are not equal. +type Result struct{ NumSame, NumDiff int } + +// BoolResult returns a Result that is either Equal or not Equal. +func BoolResult(b bool) Result { + if b { + return Result{NumSame: 1} // Equal, Similar + } else { + return Result{NumDiff: 2} // Not Equal, not Similar + } +} + +// Equal indicates whether the symbols are equal. Two symbols are equal +// if and only if NumDiff == 0. If Equal, then they are also Similar. +func (r Result) Equal() bool { return r.NumDiff == 0 } + +// Similar indicates whether two symbols are similar and may be represented +// by using the Modified type. As a special case, we consider binary comparisons +// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. +// +// The exact ratio of NumSame to NumDiff to determine similarity may change. +func (r Result) Similar() bool { + // Use NumSame+1 to offset NumSame so that binary comparisons are similar. + return r.NumSame+1 >= r.NumDiff +} + +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + +// Difference reports whether two lists of lengths nx and ny are equal +// given the definition of equality provided as f. +// +// This function returns an edit-script, which is a sequence of operations +// needed to convert one list into the other. The following invariants for +// the edit-script are maintained: +// - eq == (es.Dist()==0) +// - nx == es.LenX() +// - ny == es.LenY() +// +// This algorithm is not guaranteed to be an optimal solution (i.e., one that +// produces an edit-script with a minimal Levenshtein distance). This algorithm +// favors performance over optimality. The exact output is not guaranteed to +// be stable and may change over time. +func Difference(nx, ny int, f EqualFunc) (es EditScript) { + // This algorithm is based on traversing what is known as an "edit-graph". + // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" + // by Eugene W. Myers. Since D can be as large as N itself, this is + // effectively O(N^2). Unlike the algorithm from that paper, we are not + // interested in the optimal path, but at least some "decent" path. + // + // For example, let X and Y be lists of symbols: + // X = [A B C A B B A] + // Y = [C B A B A C] + // + // The edit-graph can be drawn as the following: + // A B C A B B A + // ┌─────────────┐ + // C │_|_|\|_|_|_|_│ 0 + // B │_|\|_|_|\|\|_│ 1 + // A │\|_|_|\|_|_|\│ 2 + // B │_|\|_|_|\|\|_│ 3 + // A │\|_|_|\|_|_|\│ 4 + // C │ | |\| | | | │ 5 + // └─────────────┘ 6 + // 0 1 2 3 4 5 6 7 + // + // List X is written along the horizontal axis, while list Y is written + // along the vertical axis. At any point on this grid, if the symbol in + // list X matches the corresponding symbol in list Y, then a '\' is drawn. + // The goal of any minimal edit-script algorithm is to find a path from the + // top-left corner to the bottom-right corner, while traveling through the + // fewest horizontal or vertical edges. + // A horizontal edge is equivalent to inserting a symbol from list X. + // A vertical edge is equivalent to inserting a symbol from list Y. + // A diagonal edge is equivalent to a matching symbol between both X and Y. + + // Invariants: + // - 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx + // - 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny + // + // In general: + // - fwdFrontier.X < revFrontier.X + // - fwdFrontier.Y < revFrontier.Y + // + // Unless, it is time for the algorithm to terminate. + fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} + revPath := path{-1, point{nx, ny}, make(EditScript, 0)} + fwdFrontier := fwdPath.point // Forward search frontier + revFrontier := revPath.point // Reverse search frontier + + // Search budget bounds the cost of searching for better paths. + // The longest sequence of non-matching symbols that can be tolerated is + // approximately the square-root of the search budget. + searchBudget := 4 * (nx + ny) // O(n) + + // Running the tests with the "cmp_debug" build tag prints a visualization + // of the algorithm running in real-time. This is educational for + // understanding how the algorithm works. See debug_enable.go. + f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) + + // The algorithm below is a greedy, meet-in-the-middle algorithm for + // computing sub-optimal edit-scripts between two lists. + // + // The algorithm is approximately as follows: + // - Searching for differences switches back-and-forth between + // a search that starts at the beginning (the top-left corner), and + // a search that starts at the end (the bottom-right corner). + // The goal of the search is connect with the search + // from the opposite corner. + // - As we search, we build a path in a greedy manner, + // where the first match seen is added to the path (this is sub-optimal, + // but provides a decent result in practice). When matches are found, + // we try the next pair of symbols in the lists and follow all matches + // as far as possible. + // - When searching for matches, we search along a diagonal going through + // through the "frontier" point. If no matches are found, + // we advance the frontier towards the opposite corner. + // - This algorithm terminates when either the X coordinates or the + // Y coordinates of the forward and reverse frontier points ever intersect. + + // This algorithm is correct even if searching only in the forward direction + // or in the reverse direction. We do both because it is commonly observed + // that two lists commonly differ because elements were added to the front + // or end of the other list. + // + // Non-deterministically start with either the forward or reverse direction + // to introduce some deliberate instability so that we have the flexibility + // to change this algorithm in the future. + if flags.Deterministic || randBool { + goto forwardSearch + } else { + goto reverseSearch + } + +forwardSearch: + { + // Forward search from the beginning. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + goto finishSearch + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{fwdFrontier.X + z, fwdFrontier.Y - z} + switch { + case p.X >= revPath.X || p.Y < fwdPath.Y: + stop1 = true // Hit top-right corner + case p.Y >= revPath.Y || p.X < fwdPath.X: + stop2 = true // Hit bottom-left corner + case f(p.X, p.Y).Equal(): + // Match found, so connect the path to this point. + fwdPath.connect(p, f) + fwdPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(fwdPath.X, fwdPath.Y).Equal() { + break + } + fwdPath.append(Identity) + } + fwdFrontier = fwdPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards reverse point. + if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { + fwdFrontier.X++ + } else { + fwdFrontier.Y++ + } + goto reverseSearch + } + +reverseSearch: + { + // Reverse search from the end. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + goto finishSearch + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{revFrontier.X - z, revFrontier.Y + z} + switch { + case fwdPath.X >= p.X || revPath.Y < p.Y: + stop1 = true // Hit bottom-left corner + case fwdPath.Y >= p.Y || revPath.X < p.X: + stop2 = true // Hit top-right corner + case f(p.X-1, p.Y-1).Equal(): + // Match found, so connect the path to this point. + revPath.connect(p, f) + revPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(revPath.X-1, revPath.Y-1).Equal() { + break + } + revPath.append(Identity) + } + revFrontier = revPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards forward point. + if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { + revFrontier.X-- + } else { + revFrontier.Y-- + } + goto forwardSearch + } + +finishSearch: + // Join the forward and reverse paths and then append the reverse path. + fwdPath.connect(revPath.point, f) + for i := len(revPath.es) - 1; i >= 0; i-- { + t := revPath.es[i] + revPath.es = revPath.es[:i] + fwdPath.append(t) + } + debug.Finish() + return fwdPath.es +} + +type path struct { + dir int // +1 if forward, -1 if reverse + point // Leading point of the EditScript path + es EditScript +} + +// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types +// to the edit-script to connect p.point to dst. +func (p *path) connect(dst point, f EqualFunc) { + if p.dir > 0 { + // Connect in forward direction. + for dst.X > p.X && dst.Y > p.Y { + switch r := f(p.X, p.Y); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case dst.X-p.X >= dst.Y-p.Y: + p.append(UniqueX) + default: + p.append(UniqueY) + } + } + for dst.X > p.X { + p.append(UniqueX) + } + for dst.Y > p.Y { + p.append(UniqueY) + } + } else { + // Connect in reverse direction. + for p.X > dst.X && p.Y > dst.Y { + switch r := f(p.X-1, p.Y-1); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case p.Y-dst.Y >= p.X-dst.X: + p.append(UniqueY) + default: + p.append(UniqueX) + } + } + for p.X > dst.X { + p.append(UniqueX) + } + for p.Y > dst.Y { + p.append(UniqueY) + } + } +} + +func (p *path) append(t EditType) { + p.es = append(p.es, t) + switch t { + case Identity, Modified: + p.add(p.dir, p.dir) + case UniqueX: + p.add(p.dir, 0) + case UniqueY: + p.add(0, p.dir) + } + debug.Update() +} + +type point struct{ X, Y int } + +func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } + +// zigzag maps a consecutive sequence of integers to a zig-zag sequence. +// +// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] +func zigzag(x int) int { + if x&1 != 0 { + x = ^x + } + return x >> 1 +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go new file mode 100644 index 00000000..d8e459c9 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go @@ -0,0 +1,9 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flags + +// Deterministic controls whether the output of Diff should be deterministic. +// This is only used for testing. +var Deterministic bool diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go new file mode 100644 index 00000000..d127d436 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go @@ -0,0 +1,99 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package function provides functionality for identifying function types. +package function + +import ( + "reflect" + "regexp" + "runtime" + "strings" +) + +type funcType int + +const ( + _ funcType = iota + + tbFunc // func(T) bool + ttbFunc // func(T, T) bool + trbFunc // func(T, R) bool + tibFunc // func(T, I) bool + trFunc // func(T) R + + Equal = ttbFunc // func(T, T) bool + EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool + Transformer = trFunc // func(T) R + ValueFilter = ttbFunc // func(T, T) bool + Less = ttbFunc // func(T, T) bool + ValuePredicate = tbFunc // func(T) bool + KeyValuePredicate = trbFunc // func(T, R) bool +) + +var boolType = reflect.TypeOf(true) + +// IsType reports whether the reflect.Type is of the specified function type. +func IsType(t reflect.Type, ft funcType) bool { + if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { + return false + } + ni, no := t.NumIn(), t.NumOut() + switch ft { + case tbFunc: // func(T) bool + if ni == 1 && no == 1 && t.Out(0) == boolType { + return true + } + case ttbFunc: // func(T, T) bool + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { + return true + } + case trbFunc: // func(T, R) bool + if ni == 2 && no == 1 && t.Out(0) == boolType { + return true + } + case tibFunc: // func(T, I) bool + if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { + return true + } + case trFunc: // func(T) R + if ni == 1 && no == 1 { + return true + } + } + return false +} + +var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) + +// NameOf returns the name of the function value. +func NameOf(v reflect.Value) string { + fnc := runtime.FuncForPC(v.Pointer()) + if fnc == nil { + return "" + } + fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" + + // Method closures have a "-fm" suffix. + fullName = strings.TrimSuffix(fullName, "-fm") + + var name string + for len(fullName) > 0 { + inParen := strings.HasSuffix(fullName, ")") + fullName = strings.TrimSuffix(fullName, ")") + + s := lastIdentRx.FindString(fullName) + if s == "" { + break + } + name = s + "." + name + fullName = strings.TrimSuffix(fullName, s) + + if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { + fullName = fullName[:i] + } + fullName = strings.TrimSuffix(fullName, ".") + } + return strings.TrimSuffix(name, ".") +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/name.go b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go new file mode 100644 index 00000000..7b498bb2 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go @@ -0,0 +1,164 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "reflect" + "strconv" +) + +var anyType = reflect.TypeOf((*interface{})(nil)).Elem() + +// TypeString is nearly identical to reflect.Type.String, +// but has an additional option to specify that full type names be used. +func TypeString(t reflect.Type, qualified bool) string { + return string(appendTypeName(nil, t, qualified, false)) +} + +func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { + // BUG: Go reflection provides no way to disambiguate two named types + // of the same name and within the same package, + // but declared within the namespace of different functions. + + // Use the "any" alias instead of "interface{}" for better readability. + if t == anyType { + return append(b, "any"...) + } + + // Named type. + if t.Name() != "" { + if qualified && t.PkgPath() != "" { + b = append(b, '"') + b = append(b, t.PkgPath()...) + b = append(b, '"') + b = append(b, '.') + b = append(b, t.Name()...) + } else { + b = append(b, t.String()...) + } + return b + } + + // Unnamed type. + switch k := t.Kind(); k { + case reflect.Bool, reflect.String, reflect.UnsafePointer, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + b = append(b, k.String()...) + case reflect.Chan: + if t.ChanDir() == reflect.RecvDir { + b = append(b, "<-"...) + } + b = append(b, "chan"...) + if t.ChanDir() == reflect.SendDir { + b = append(b, "<-"...) + } + b = append(b, ' ') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Func: + if !elideFunc { + b = append(b, "func"...) + } + b = append(b, '(') + for i := 0; i < t.NumIn(); i++ { + if i > 0 { + b = append(b, ", "...) + } + if i == t.NumIn()-1 && t.IsVariadic() { + b = append(b, "..."...) + b = appendTypeName(b, t.In(i).Elem(), qualified, false) + } else { + b = appendTypeName(b, t.In(i), qualified, false) + } + } + b = append(b, ')') + switch t.NumOut() { + case 0: + // Do nothing + case 1: + b = append(b, ' ') + b = appendTypeName(b, t.Out(0), qualified, false) + default: + b = append(b, " ("...) + for i := 0; i < t.NumOut(); i++ { + if i > 0 { + b = append(b, ", "...) + } + b = appendTypeName(b, t.Out(i), qualified, false) + } + b = append(b, ')') + } + case reflect.Struct: + b = append(b, "struct{ "...) + for i := 0; i < t.NumField(); i++ { + if i > 0 { + b = append(b, "; "...) + } + sf := t.Field(i) + if !sf.Anonymous { + if qualified && sf.PkgPath != "" { + b = append(b, '"') + b = append(b, sf.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, sf.Name...) + b = append(b, ' ') + } + b = appendTypeName(b, sf.Type, qualified, false) + if sf.Tag != "" { + b = append(b, ' ') + b = strconv.AppendQuote(b, string(sf.Tag)) + } + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + case reflect.Slice, reflect.Array: + b = append(b, '[') + if k == reflect.Array { + b = strconv.AppendUint(b, uint64(t.Len()), 10) + } + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Map: + b = append(b, "map["...) + b = appendTypeName(b, t.Key(), qualified, false) + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Ptr: + b = append(b, '*') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Interface: + b = append(b, "interface{ "...) + for i := 0; i < t.NumMethod(); i++ { + if i > 0 { + b = append(b, "; "...) + } + m := t.Method(i) + if qualified && m.PkgPath != "" { + b = append(b, '"') + b = append(b, m.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, m.Name...) + b = appendTypeName(b, m.Type, qualified, true) + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + default: + panic("invalid kind: " + k.String()) + } + return b +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go new file mode 100644 index 00000000..1a71bfcb --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go @@ -0,0 +1,34 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build purego +// +build purego + +package value + +import "reflect" + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p uintptr + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // NOTE: Storing a pointer as an uintptr is technically incorrect as it + // assumes that the GC implementation does not use a moving collector. + return Pointer{v.Pointer(), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == 0 +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return p.p +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go new file mode 100644 index 00000000..16e6860a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go @@ -0,0 +1,37 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !purego +// +build !purego + +package value + +import ( + "reflect" + "unsafe" +) + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p unsafe.Pointer + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // The proper representation of a pointer is unsafe.Pointer, + // which is necessary if the GC ever uses a moving collector. + return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == nil +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return uintptr(p.p) +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go new file mode 100644 index 00000000..98533b03 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -0,0 +1,106 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "fmt" + "math" + "reflect" + "sort" +) + +// SortKeys sorts a list of map keys, deduplicating keys if necessary. +// The type of each value must be comparable. +func SortKeys(vs []reflect.Value) []reflect.Value { + if len(vs) == 0 { + return vs + } + + // Sort the map keys. + sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) + + // Deduplicate keys (fails for NaNs). + vs2 := vs[:1] + for _, v := range vs[1:] { + if isLess(vs2[len(vs2)-1], v) { + vs2 = append(vs2, v) + } + } + return vs2 +} + +// isLess is a generic function for sorting arbitrary map keys. +// The inputs must be of the same type and must be comparable. +func isLess(x, y reflect.Value) bool { + switch x.Type().Kind() { + case reflect.Bool: + return !x.Bool() && y.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() < y.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return x.Uint() < y.Uint() + case reflect.Float32, reflect.Float64: + // NOTE: This does not sort -0 as less than +0 + // since Go maps treat -0 and +0 as equal keys. + fx, fy := x.Float(), y.Float() + return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) + case reflect.Complex64, reflect.Complex128: + cx, cy := x.Complex(), y.Complex() + rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) + if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { + return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) + } + return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) + case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: + return x.Pointer() < y.Pointer() + case reflect.String: + return x.String() < y.String() + case reflect.Array: + for i := 0; i < x.Len(); i++ { + if isLess(x.Index(i), y.Index(i)) { + return true + } + if isLess(y.Index(i), x.Index(i)) { + return false + } + } + return false + case reflect.Struct: + for i := 0; i < x.NumField(); i++ { + if isLess(x.Field(i), y.Field(i)) { + return true + } + if isLess(y.Field(i), x.Field(i)) { + return false + } + } + return false + case reflect.Interface: + vx, vy := x.Elem(), y.Elem() + if !vx.IsValid() || !vy.IsValid() { + return !vx.IsValid() && vy.IsValid() + } + tx, ty := vx.Type(), vy.Type() + if tx == ty { + return isLess(x.Elem(), y.Elem()) + } + if tx.Kind() != ty.Kind() { + return vx.Kind() < vy.Kind() + } + if tx.String() != ty.String() { + return tx.String() < ty.String() + } + if tx.PkgPath() != ty.PkgPath() { + return tx.PkgPath() < ty.PkgPath() + } + // This can happen in rare situations, so we fallback to just comparing + // the unique pointer for a reflect.Type. This guarantees deterministic + // ordering within a program, but it is obviously not stable. + return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() + default: + // Must be Func, Map, or Slice; which are not comparable. + panic(fmt.Sprintf("%T is not comparable", x.Type())) + } +} diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go new file mode 100644 index 00000000..1f9ca9c4 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -0,0 +1,554 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/google/go-cmp/cmp/internal/function" +) + +// Option configures for specific behavior of Equal and Diff. In particular, +// the fundamental Option functions (Ignore, Transformer, and Comparer), +// configure how equality is determined. +// +// The fundamental options may be composed with filters (FilterPath and +// FilterValues) to control the scope over which they are applied. +// +// The cmp/cmpopts package provides helper functions for creating options that +// may be used with Equal and Diff. +type Option interface { + // filter applies all filters and returns the option that remains. + // Each option may only read s.curPath and call s.callTTBFunc. + // + // An Options is returned only if multiple comparers or transformers + // can apply simultaneously and will only contain values of those types + // or sub-Options containing values of those types. + filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption +} + +// applicableOption represents the following types: +// +// Fundamental: ignore | validator | *comparer | *transformer +// Grouping: Options +type applicableOption interface { + Option + + // apply executes the option, which may mutate s or panic. + apply(s *state, vx, vy reflect.Value) +} + +// coreOption represents the following types: +// +// Fundamental: ignore | validator | *comparer | *transformer +// Filters: *pathFilter | *valuesFilter +type coreOption interface { + Option + isCore() +} + +type core struct{} + +func (core) isCore() {} + +// Options is a list of Option values that also satisfies the Option interface. +// Helper comparison packages may return an Options value when packing multiple +// Option values into a single Option. When this package processes an Options, +// it will be implicitly expanded into a flat list. +// +// Applying a filter on an Options is equivalent to applying that same filter +// on all individual options held within. +type Options []Option + +func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { + for _, opt := range opts { + switch opt := opt.filter(s, t, vx, vy); opt.(type) { + case ignore: + return ignore{} // Only ignore can short-circuit evaluation + case validator: + out = validator{} // Takes precedence over comparer or transformer + case *comparer, *transformer, Options: + switch out.(type) { + case nil: + out = opt + case validator: + // Keep validator + case *comparer, *transformer, Options: + out = Options{out, opt} // Conflicting comparers or transformers + } + } + } + return out +} + +func (opts Options) apply(s *state, _, _ reflect.Value) { + const warning = "ambiguous set of applicable options" + const help = "consider using filters to ensure at most one Comparer or Transformer may apply" + var ss []string + for _, opt := range flattenOptions(nil, opts) { + ss = append(ss, fmt.Sprint(opt)) + } + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) +} + +func (opts Options) String() string { + var ss []string + for _, opt := range opts { + ss = append(ss, fmt.Sprint(opt)) + } + return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) +} + +// FilterPath returns a new Option where opt is only evaluated if filter f +// returns true for the current Path in the value tree. +// +// This filter is called even if a slice element or map entry is missing and +// provides an opportunity to ignore such cases. The filter function must be +// symmetric such that the filter result is identical regardless of whether the +// missing value is from x or y. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterPath(f func(Path) bool, opt Option) Option { + if f == nil { + panic("invalid path filter function") + } + if opt := normalizeOption(opt); opt != nil { + return &pathFilter{fnc: f, opt: opt} + } + return nil +} + +type pathFilter struct { + core + fnc func(Path) bool + opt Option +} + +func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if f.fnc(s.curPath) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f pathFilter) String() string { + return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) +} + +// FilterValues returns a new Option where opt is only evaluated if filter f, +// which is a function of the form "func(T, T) bool", returns true for the +// current pair of values being compared. If either value is invalid or +// the type of the values is not assignable to T, then this filter implicitly +// returns false. +// +// The filter function must be +// symmetric (i.e., agnostic to the order of the inputs) and +// deterministic (i.e., produces the same result when given the same inputs). +// If T is an interface, it is possible that f is called with two values with +// different concrete types that both implement T. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterValues(f interface{}, opt Option) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { + panic(fmt.Sprintf("invalid values filter function: %T", f)) + } + if opt := normalizeOption(opt); opt != nil { + vf := &valuesFilter{fnc: v, opt: opt} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + vf.typ = ti + } + return vf + } + return nil +} + +type valuesFilter struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool + opt Option +} + +func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { + return nil + } + if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f valuesFilter) String() string { + return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) +} + +// Ignore is an Option that causes all comparisons to be ignored. +// This value is intended to be combined with FilterPath or FilterValues. +// It is an error to pass an unfiltered Ignore option to Equal. +func Ignore() Option { return ignore{} } + +type ignore struct{ core } + +func (ignore) isFiltered() bool { return false } +func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } +func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } +func (ignore) String() string { return "Ignore()" } + +// validator is a sentinel Option type to indicate that some options could not +// be evaluated due to unexported fields, missing slice elements, or +// missing map entries. Both values are validator only for unexported fields. +type validator struct{ core } + +func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vy.IsValid() { + return validator{} + } + if !vx.CanInterface() || !vy.CanInterface() { + return validator{} + } + return nil +} +func (validator) apply(s *state, vx, vy reflect.Value) { + // Implies missing slice element or map entry. + if !vx.IsValid() || !vy.IsValid() { + s.report(vx.IsValid() == vy.IsValid(), 0) + return + } + + // Unable to Interface implies unexported field without visibility access. + if !vx.CanInterface() || !vy.CanInterface() { + help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" + var name string + if t := s.curPath.Index(-2).Type(); t.Name() != "" { + // Named type with unexported fields. + name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType + if _, ok := reflect.New(t).Interface().(error); ok { + help = "consider using cmpopts.EquateErrors to compare error values" + } + } else { + // Unnamed type with unexported fields. Derive PkgPath from field. + var pkgPath string + for i := 0; i < t.NumField() && pkgPath == ""; i++ { + pkgPath = t.Field(i).PkgPath + } + name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) + } + panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) + } + + panic("not reachable") +} + +// identRx represents a valid identifier according to the Go specification. +const identRx = `[_\p{L}][_\p{L}\p{N}]*` + +var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) + +// Transformer returns an Option that applies a transformation function that +// converts values of a certain type into that of another. +// +// The transformer f must be a function "func(T) R" that converts values of +// type T to those of type R and is implicitly filtered to input values +// assignable to T. The transformer must not mutate T in any way. +// +// To help prevent some cases of infinite recursive cycles applying the +// same transform to the output of itself (e.g., in the case where the +// input and output types are the same), an implicit filter is added such that +// a transformer is applicable only if that exact transformer is not already +// in the tail of the Path since the last non-Transform step. +// For situations where the implicit filter is still insufficient, +// consider using cmpopts.AcyclicTransformer, which adds a filter +// to prevent the transformer from being recursively applied upon itself. +// +// The name is a user provided label that is used as the Transform.Name in the +// transformation PathStep (and eventually shown in the Diff output). +// The name must be a valid identifier or qualified identifier in Go syntax. +// If empty, an arbitrary name is used. +func Transformer(name string, f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { + panic(fmt.Sprintf("invalid transformer function: %T", f)) + } + if name == "" { + name = function.NameOf(v) + if !identsRx.MatchString(name) { + name = "λ" // Lambda-symbol as placeholder name + } + } else if !identsRx.MatchString(name) { + panic(fmt.Sprintf("invalid name: %q", name)) + } + tr := &transformer{name: name, fnc: reflect.ValueOf(f)} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + tr.typ = ti + } + return tr +} + +type transformer struct { + core + name string + typ reflect.Type // T + fnc reflect.Value // func(T) R +} + +func (tr *transformer) isFiltered() bool { return tr.typ != nil } + +func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { + for i := len(s.curPath) - 1; i >= 0; i-- { + if t, ok := s.curPath[i].(Transform); !ok { + break // Hit most recent non-Transform step + } else if tr == t.trans { + return nil // Cannot directly use same Transform + } + } + if tr.typ == nil || t.AssignableTo(tr.typ) { + return tr + } + return nil +} + +func (tr *transformer) apply(s *state, vx, vy reflect.Value) { + step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} + vvx := s.callTRFunc(tr.fnc, vx, step) + vvy := s.callTRFunc(tr.fnc, vy, step) + step.vx, step.vy = vvx, vvy + s.compareAny(step) +} + +func (tr transformer) String() string { + return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) +} + +// Comparer returns an Option that determines whether two values are equal +// to each other. +// +// The comparer f must be a function "func(T, T) bool" and is implicitly +// filtered to input values assignable to T. If T is an interface, it is +// possible that f is called with two values of different concrete types that +// both implement T. +// +// The equality function must be: +// - Symmetric: equal(x, y) == equal(y, x) +// - Deterministic: equal(x, y) == equal(x, y) +// - Pure: equal(x, y) does not modify x or y +func Comparer(f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Equal) || v.IsNil() { + panic(fmt.Sprintf("invalid comparer function: %T", f)) + } + cm := &comparer{fnc: v} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + cm.typ = ti + } + return cm +} + +type comparer struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (cm *comparer) isFiltered() bool { return cm.typ != nil } + +func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { + if cm.typ == nil || t.AssignableTo(cm.typ) { + return cm + } + return nil +} + +func (cm *comparer) apply(s *state, vx, vy reflect.Value) { + eq := s.callTTBFunc(cm.fnc, vx, vy) + s.report(eq, reportByFunc) +} + +func (cm comparer) String() string { + return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) +} + +// Exporter returns an Option that specifies whether Equal is allowed to +// introspect into the unexported fields of certain struct types. +// +// Users of this option must understand that comparing on unexported fields +// from external packages is not safe since changes in the internal +// implementation of some external package may cause the result of Equal +// to unexpectedly change. However, it may be valid to use this option on types +// defined in an internal package where the semantic meaning of an unexported +// field is in the control of the user. +// +// In many cases, a custom Comparer should be used instead that defines +// equality as a function of the public API of a type rather than the underlying +// unexported implementation. +// +// For example, the reflect.Type documentation defines equality to be determined +// by the == operator on the interface (essentially performing a shallow pointer +// comparison) and most attempts to compare *regexp.Regexp types are interested +// in only checking that the regular expression strings are equal. +// Both of these are accomplished using Comparers: +// +// Comparer(func(x, y reflect.Type) bool { return x == y }) +// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) +// +// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore +// all unexported fields on specified struct types. +func Exporter(f func(reflect.Type) bool) Option { + if !supportExporters { + panic("Exporter is not supported on purego builds") + } + return exporter(f) +} + +type exporter func(reflect.Type) bool + +func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// AllowUnexported returns an Options that allows Equal to forcibly introspect +// unexported fields of the specified struct types. +// +// See Exporter for the proper use of this option. +func AllowUnexported(types ...interface{}) Option { + m := make(map[reflect.Type]bool) + for _, typ := range types { + t := reflect.TypeOf(typ) + if t.Kind() != reflect.Struct { + panic(fmt.Sprintf("invalid struct type: %T", typ)) + } + m[t] = true + } + return exporter(func(t reflect.Type) bool { return m[t] }) +} + +// Result represents the comparison result for a single node and +// is provided by cmp when calling Report (see Reporter). +type Result struct { + _ [0]func() // Make Result incomparable + flags resultFlags +} + +// Equal reports whether the node was determined to be equal or not. +// As a special case, ignored nodes are considered equal. +func (r Result) Equal() bool { + return r.flags&(reportEqual|reportByIgnore) != 0 +} + +// ByIgnore reports whether the node is equal because it was ignored. +// This never reports true if Equal reports false. +func (r Result) ByIgnore() bool { + return r.flags&reportByIgnore != 0 +} + +// ByMethod reports whether the Equal method determined equality. +func (r Result) ByMethod() bool { + return r.flags&reportByMethod != 0 +} + +// ByFunc reports whether a Comparer function determined equality. +func (r Result) ByFunc() bool { + return r.flags&reportByFunc != 0 +} + +// ByCycle reports whether a reference cycle was detected. +func (r Result) ByCycle() bool { + return r.flags&reportByCycle != 0 +} + +type resultFlags uint + +const ( + _ resultFlags = (1 << iota) / 2 + + reportEqual + reportUnequal + reportByIgnore + reportByMethod + reportByFunc + reportByCycle +) + +// Reporter is an Option that can be passed to Equal. When Equal traverses +// the value trees, it calls PushStep as it descends into each node in the +// tree and PopStep as it ascend out of the node. The leaves of the tree are +// either compared (determined to be equal or not equal) or ignored and reported +// as such by calling the Report method. +func Reporter(r interface { + // PushStep is called when a tree-traversal operation is performed. + // The PathStep itself is only valid until the step is popped. + // The PathStep.Values are valid for the duration of the entire traversal + // and must not be mutated. + // + // Equal always calls PushStep at the start to provide an operation-less + // PathStep used to report the root values. + // + // Within a slice, the exact set of inserted, removed, or modified elements + // is unspecified and may change in future implementations. + // The entries of a map are iterated through in an unspecified order. + PushStep(PathStep) + + // Report is called exactly once on leaf nodes to report whether the + // comparison identified the node as equal, unequal, or ignored. + // A leaf node is one that is immediately preceded by and followed by + // a pair of PushStep and PopStep calls. + Report(Result) + + // PopStep ascends back up the value tree. + // There is always a matching pop call for every push call. + PopStep() +}) Option { + return reporter{r} +} + +type reporter struct{ reporterIface } +type reporterIface interface { + PushStep(PathStep) + Report(Result) + PopStep() +} + +func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// normalizeOption normalizes the input options such that all Options groups +// are flattened and groups with a single element are reduced to that element. +// Only coreOptions and Options containing coreOptions are allowed. +func normalizeOption(src Option) Option { + switch opts := flattenOptions(nil, Options{src}); len(opts) { + case 0: + return nil + case 1: + return opts[0] + default: + return opts + } +} + +// flattenOptions copies all options in src to dst as a flat list. +// Only coreOptions and Options containing coreOptions are allowed. +func flattenOptions(dst, src Options) Options { + for _, opt := range src { + switch opt := opt.(type) { + case nil: + continue + case Options: + dst = flattenOptions(dst, opt) + case coreOption: + dst = append(dst, opt) + default: + panic(fmt.Sprintf("invalid option type: %T", opt)) + } + } + return dst +} diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go new file mode 100644 index 00000000..a0a58850 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/path.go @@ -0,0 +1,380 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +// Path is a list of PathSteps describing the sequence of operations to get +// from some root type to the current position in the value tree. +// The first Path element is always an operation-less PathStep that exists +// simply to identify the initial type. +// +// When traversing structs with embedded structs, the embedded struct will +// always be accessed as a field before traversing the fields of the +// embedded struct themselves. That is, an exported field from the +// embedded struct will never be accessed directly from the parent struct. +type Path []PathStep + +// PathStep is a union-type for specific operations to traverse +// a value's tree structure. Users of this package never need to implement +// these types as values of this type will be returned by this package. +// +// Implementations of this interface are +// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. +type PathStep interface { + String() string + + // Type is the resulting type after performing the path step. + Type() reflect.Type + + // Values is the resulting values after performing the path step. + // The type of each valid value is guaranteed to be identical to Type. + // + // In some cases, one or both may be invalid or have restrictions: + // - For StructField, both are not interface-able if the current field + // is unexported and the struct type is not explicitly permitted by + // an Exporter to traverse unexported fields. + // - For SliceIndex, one may be invalid if an element is missing from + // either the x or y slice. + // - For MapIndex, one may be invalid if an entry is missing from + // either the x or y map. + // + // The provided values must not be mutated. + Values() (vx, vy reflect.Value) +} + +var ( + _ PathStep = StructField{} + _ PathStep = SliceIndex{} + _ PathStep = MapIndex{} + _ PathStep = Indirect{} + _ PathStep = TypeAssertion{} + _ PathStep = Transform{} +) + +func (pa *Path) push(s PathStep) { + *pa = append(*pa, s) +} + +func (pa *Path) pop() { + *pa = (*pa)[:len(*pa)-1] +} + +// Last returns the last PathStep in the Path. +// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Last() PathStep { + return pa.Index(-1) +} + +// Index returns the ith step in the Path and supports negative indexing. +// A negative index starts counting from the tail of the Path such that -1 +// refers to the last step, -2 refers to the second-to-last step, and so on. +// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Index(i int) PathStep { + if i < 0 { + i = len(pa) + i + } + if i < 0 || i >= len(pa) { + return pathStep{} + } + return pa[i] +} + +// String returns the simplified path to a node. +// The simplified path only contains struct field accesses. +// +// For example: +// +// MyMap.MySlices.MyField +func (pa Path) String() string { + var ss []string + for _, s := range pa { + if _, ok := s.(StructField); ok { + ss = append(ss, s.String()) + } + } + return strings.TrimPrefix(strings.Join(ss, ""), ".") +} + +// GoString returns the path to a specific node using Go syntax. +// +// For example: +// +// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField +func (pa Path) GoString() string { + var ssPre, ssPost []string + var numIndirect int + for i, s := range pa { + var nextStep PathStep + if i+1 < len(pa) { + nextStep = pa[i+1] + } + switch s := s.(type) { + case Indirect: + numIndirect++ + pPre, pPost := "(", ")" + switch nextStep.(type) { + case Indirect: + continue // Next step is indirection, so let them batch up + case StructField: + numIndirect-- // Automatic indirection on struct fields + case nil: + pPre, pPost = "", "" // Last step; no need for parenthesis + } + if numIndirect > 0 { + ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) + ssPost = append(ssPost, pPost) + } + numIndirect = 0 + continue + case Transform: + ssPre = append(ssPre, s.trans.name+"(") + ssPost = append(ssPost, ")") + continue + } + ssPost = append(ssPost, s.String()) + } + for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { + ssPre[i], ssPre[j] = ssPre[j], ssPre[i] + } + return strings.Join(ssPre, "") + strings.Join(ssPost, "") +} + +type pathStep struct { + typ reflect.Type + vx, vy reflect.Value +} + +func (ps pathStep) Type() reflect.Type { return ps.typ } +func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } +func (ps pathStep) String() string { + if ps.typ == nil { + return "" + } + s := value.TypeString(ps.typ, false) + if s == "" || strings.ContainsAny(s, "{}\n") { + return "root" // Type too simple or complex to print + } + return fmt.Sprintf("{%s}", s) +} + +// StructField represents a struct field access on a field called Name. +type StructField struct{ *structField } +type structField struct { + pathStep + name string + idx int + + // These fields are used for forcibly accessing an unexported field. + // pvx, pvy, and field are only valid if unexported is true. + unexported bool + mayForce bool // Forcibly allow visibility + paddr bool // Was parent addressable? + pvx, pvy reflect.Value // Parent values (always addressable) + field reflect.StructField // Field information +} + +func (sf StructField) Type() reflect.Type { return sf.typ } +func (sf StructField) Values() (vx, vy reflect.Value) { + if !sf.unexported { + return sf.vx, sf.vy // CanInterface reports true + } + + // Forcibly obtain read-write access to an unexported struct field. + if sf.mayForce { + vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) + vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) + return vx, vy // CanInterface reports true + } + return sf.vx, sf.vy // CanInterface reports false +} +func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } + +// Name is the field name. +func (sf StructField) Name() string { return sf.name } + +// Index is the index of the field in the parent struct type. +// See reflect.Type.Field. +func (sf StructField) Index() int { return sf.idx } + +// SliceIndex is an index operation on a slice or array at some index Key. +type SliceIndex struct{ *sliceIndex } +type sliceIndex struct { + pathStep + xkey, ykey int + isSlice bool // False for reflect.Array +} + +func (si SliceIndex) Type() reflect.Type { return si.typ } +func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } +func (si SliceIndex) String() string { + switch { + case si.xkey == si.ykey: + return fmt.Sprintf("[%d]", si.xkey) + case si.ykey == -1: + // [5->?] means "I don't know where X[5] went" + return fmt.Sprintf("[%d->?]", si.xkey) + case si.xkey == -1: + // [?->3] means "I don't know where Y[3] came from" + return fmt.Sprintf("[?->%d]", si.ykey) + default: + // [5->3] means "X[5] moved to Y[3]" + return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) + } +} + +// Key is the index key; it may return -1 if in a split state +func (si SliceIndex) Key() int { + if si.xkey != si.ykey { + return -1 + } + return si.xkey +} + +// SplitKeys are the indexes for indexing into slices in the +// x and y values, respectively. These indexes may differ due to the +// insertion or removal of an element in one of the slices, causing +// all of the indexes to be shifted. If an index is -1, then that +// indicates that the element does not exist in the associated slice. +// +// Key is guaranteed to return -1 if and only if the indexes returned +// by SplitKeys are not the same. SplitKeys will never return -1 for +// both indexes. +func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } + +// MapIndex is an index operation on a map at some index Key. +type MapIndex struct{ *mapIndex } +type mapIndex struct { + pathStep + key reflect.Value +} + +func (mi MapIndex) Type() reflect.Type { return mi.typ } +func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } +func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } + +// Key is the value of the map key. +func (mi MapIndex) Key() reflect.Value { return mi.key } + +// Indirect represents pointer indirection on the parent type. +type Indirect struct{ *indirect } +type indirect struct { + pathStep +} + +func (in Indirect) Type() reflect.Type { return in.typ } +func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } +func (in Indirect) String() string { return "*" } + +// TypeAssertion represents a type assertion on an interface. +type TypeAssertion struct{ *typeAssertion } +type typeAssertion struct { + pathStep +} + +func (ta TypeAssertion) Type() reflect.Type { return ta.typ } +func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } +func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } + +// Transform is a transformation from the parent type to the current type. +type Transform struct{ *transform } +type transform struct { + pathStep + trans *transformer +} + +func (tf Transform) Type() reflect.Type { return tf.typ } +func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } +func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } + +// Name is the name of the Transformer. +func (tf Transform) Name() string { return tf.trans.name } + +// Func is the function pointer to the transformer function. +func (tf Transform) Func() reflect.Value { return tf.trans.fnc } + +// Option returns the originally constructed Transformer option. +// The == operator can be used to detect the exact option used. +func (tf Transform) Option() Option { return tf.trans } + +// pointerPath represents a dual-stack of pointers encountered when +// recursively traversing the x and y values. This data structure supports +// detection of cycles and determining whether the cycles are equal. +// In Go, cycles can occur via pointers, slices, and maps. +// +// The pointerPath uses a map to represent a stack; where descension into a +// pointer pushes the address onto the stack, and ascension from a pointer +// pops the address from the stack. Thus, when traversing into a pointer from +// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles +// by checking whether the pointer has already been visited. The cycle detection +// uses a separate stack for the x and y values. +// +// If a cycle is detected we need to determine whether the two pointers +// should be considered equal. The definition of equality chosen by Equal +// requires two graphs to have the same structure. To determine this, both the +// x and y values must have a cycle where the previous pointers were also +// encountered together as a pair. +// +// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and +// MapIndex with pointer information for the x and y values. +// Suppose px and py are two pointers to compare, we then search the +// Path for whether px was ever encountered in the Path history of x, and +// similarly so with py. If either side has a cycle, the comparison is only +// equal if both px and py have a cycle resulting from the same PathStep. +// +// Using a map as a stack is more performant as we can perform cycle detection +// in O(1) instead of O(N) where N is len(Path). +type pointerPath struct { + // mx is keyed by x pointers, where the value is the associated y pointer. + mx map[value.Pointer]value.Pointer + // my is keyed by y pointers, where the value is the associated x pointer. + my map[value.Pointer]value.Pointer +} + +func (p *pointerPath) Init() { + p.mx = make(map[value.Pointer]value.Pointer) + p.my = make(map[value.Pointer]value.Pointer) +} + +// Push indicates intent to descend into pointers vx and vy where +// visited reports whether either has been seen before. If visited before, +// equal reports whether both pointers were encountered together. +// Pop must be called if and only if the pointers were never visited. +// +// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map +// and be non-nil. +func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { + px := value.PointerOf(vx) + py := value.PointerOf(vy) + _, ok1 := p.mx[px] + _, ok2 := p.my[py] + if ok1 || ok2 { + equal = p.mx[px] == py && p.my[py] == px // Pointers paired together + return equal, true + } + p.mx[px] = py + p.my[py] = px + return false, false +} + +// Pop ascends from pointers vx and vy. +func (p pointerPath) Pop(vx, vy reflect.Value) { + delete(p.mx, value.PointerOf(vx)) + delete(p.my, value.PointerOf(vy)) +} + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go new file mode 100644 index 00000000..f43cd12e --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report.go @@ -0,0 +1,54 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +// defaultReporter implements the reporter interface. +// +// As Equal serially calls the PushStep, Report, and PopStep methods, the +// defaultReporter constructs a tree-based representation of the compared value +// and the result of each comparison (see valueNode). +// +// When the String method is called, the FormatDiff method transforms the +// valueNode tree into a textNode tree, which is a tree-based representation +// of the textual output (see textNode). +// +// Lastly, the textNode.String method produces the final report as a string. +type defaultReporter struct { + root *valueNode + curr *valueNode +} + +func (r *defaultReporter) PushStep(ps PathStep) { + r.curr = r.curr.PushStep(ps) + if r.root == nil { + r.root = r.curr + } +} +func (r *defaultReporter) Report(rs Result) { + r.curr.Report(rs) +} +func (r *defaultReporter) PopStep() { + r.curr = r.curr.PopStep() +} + +// String provides a full report of the differences detected as a structured +// literal in pseudo-Go syntax. String may only be called after the entire tree +// has been traversed. +func (r *defaultReporter) String() string { + assert(r.root != nil && r.curr == nil) + if r.root.NumDiff == 0 { + return "" + } + ptrs := new(pointerReferences) + text := formatOptions{}.FormatDiff(r.root, ptrs) + resolveReferences(text) + return text.String() +} + +func assert(ok bool) { + if !ok { + panic("assertion failure") + } +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go new file mode 100644 index 00000000..2050bf6b --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go @@ -0,0 +1,433 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" +) + +// numContextRecords is the number of surrounding equal records to print. +const numContextRecords = 2 + +type diffMode byte + +const ( + diffUnknown diffMode = 0 + diffIdentical diffMode = ' ' + diffRemoved diffMode = '-' + diffInserted diffMode = '+' +) + +type typeMode int + +const ( + // emitType always prints the type. + emitType typeMode = iota + // elideType never prints the type. + elideType + // autoType prints the type only for composite kinds + // (i.e., structs, slices, arrays, and maps). + autoType +) + +type formatOptions struct { + // DiffMode controls the output mode of FormatDiff. + // + // If diffUnknown, then produce a diff of the x and y values. + // If diffIdentical, then emit values as if they were equal. + // If diffRemoved, then only emit x values (ignoring y values). + // If diffInserted, then only emit y values (ignoring x values). + DiffMode diffMode + + // TypeMode controls whether to print the type for the current node. + // + // As a general rule of thumb, we always print the type of the next node + // after an interface, and always elide the type of the next node after + // a slice or map node. + TypeMode typeMode + + // formatValueOptions are options specific to printing reflect.Values. + formatValueOptions +} + +func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { + opts.DiffMode = d + return opts +} +func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { + opts.TypeMode = t + return opts +} +func (opts formatOptions) WithVerbosity(level int) formatOptions { + opts.VerbosityLevel = level + opts.LimitVerbosity = true + return opts +} +func (opts formatOptions) verbosity() uint { + switch { + case opts.VerbosityLevel < 0: + return 0 + case opts.VerbosityLevel > 16: + return 16 // some reasonable maximum to avoid shift overflow + default: + return uint(opts.VerbosityLevel) + } +} + +const maxVerbosityPreset = 6 + +// verbosityPreset modifies the verbosity settings given an index +// between 0 and maxVerbosityPreset, inclusive. +func verbosityPreset(opts formatOptions, i int) formatOptions { + opts.VerbosityLevel = int(opts.verbosity()) + 2*i + if i > 0 { + opts.AvoidStringer = true + } + if i >= maxVerbosityPreset { + opts.PrintAddresses = true + opts.QualifiedNames = true + } + return opts +} + +// FormatDiff converts a valueNode tree into a textNode tree, where the later +// is a textual representation of the differences detected in the former. +func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { + if opts.DiffMode == diffIdentical { + opts = opts.WithVerbosity(1) + } else if opts.verbosity() < 3 { + opts = opts.WithVerbosity(3) + } + + // Check whether we have specialized formatting for this node. + // This is not necessary, but helpful for producing more readable outputs. + if opts.CanFormatDiffSlice(v) { + return opts.FormatDiffSlice(v) + } + + var parentKind reflect.Kind + if v.parent != nil && v.parent.TransformerName == "" { + parentKind = v.parent.Type.Kind() + } + + // For leaf nodes, format the value based on the reflect.Values alone. + // As a special case, treat equal []byte as a leaf nodes. + isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == byteType + isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0 + if v.MaxDepth == 0 || isEqualBytes { + switch opts.DiffMode { + case diffUnknown, diffIdentical: + // Format Equal. + if v.NumDiff == 0 { + outx := opts.FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.FormatValue(v.ValueY, parentKind, ptrs) + if v.NumIgnored > 0 && v.NumSame == 0 { + return textEllipsis + } else if outx.Len() < outy.Len() { + return outx + } else { + return outy + } + } + + // Format unequal. + assert(opts.DiffMode == diffUnknown) + var list textList + outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) + outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) + outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: '-', Value: outx}) + } + if outy != nil { + list = append(list, textRecord{Diff: '+', Value: outy}) + } + return opts.WithTypeMode(emitType).FormatType(v.Type, list) + case diffRemoved: + return opts.FormatValue(v.ValueX, parentKind, ptrs) + case diffInserted: + return opts.FormatValue(v.ValueY, parentKind, ptrs) + default: + panic("invalid diff mode") + } + } + + // Register slice element to support cycle detection. + if parentKind == reflect.Slice { + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) + defer ptrs.Pop() + defer func() { out = wrapTrunkReferences(ptrRefs, out) }() + } + + // Descend into the child value node. + if v.TransformerName != "" { + out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} + return opts.FormatType(v.Type, out) + } else { + switch k := v.Type.Kind(); k { + case reflect.Struct, reflect.Array, reflect.Slice: + out = opts.formatDiffList(v.Records, k, ptrs) + out = opts.FormatType(v.Type, out) + case reflect.Map: + // Register map to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.formatDiffList(v.Records, k, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = opts.FormatType(v.Type, out) + case reflect.Ptr: + // Register pointer to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.FormatDiff(v.Value, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = &textWrap{Prefix: "&", Value: out} + case reflect.Interface: + out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + default: + panic(fmt.Sprintf("%v cannot have children", k)) + } + return out + } +} + +func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { + // Derive record name based on the data structure kind. + var name string + var formatKey func(reflect.Value) string + switch k { + case reflect.Struct: + name = "field" + opts = opts.WithTypeMode(autoType) + formatKey = func(v reflect.Value) string { return v.String() } + case reflect.Slice, reflect.Array: + name = "element" + opts = opts.WithTypeMode(elideType) + formatKey = func(reflect.Value) string { return "" } + case reflect.Map: + name = "entry" + opts = opts.WithTypeMode(elideType) + formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } + } + + maxLen := -1 + if opts.LimitVerbosity { + if opts.DiffMode == diffIdentical { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + } else { + maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... + } + opts.VerbosityLevel-- + } + + // Handle unification. + switch opts.DiffMode { + case diffIdentical, diffRemoved, diffInserted: + var list textList + var deferredEllipsis bool // Add final "..." to indicate records were dropped + for _, r := range recs { + if len(list) == maxLen { + deferredEllipsis = true + break + } + + // Elide struct fields that are zero value. + if k == reflect.Struct { + var isZero bool + switch opts.DiffMode { + case diffIdentical: + isZero = r.Value.ValueX.IsZero() || r.Value.ValueY.IsZero() + case diffRemoved: + isZero = r.Value.ValueX.IsZero() + case diffInserted: + isZero = r.Value.ValueY.IsZero() + } + if isZero { + continue + } + } + // Elide ignored nodes. + if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { + deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) + if !deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + continue + } + if out := opts.FormatDiff(r.Value, ptrs); out != nil { + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + } + } + if deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case diffUnknown: + default: + panic("invalid diff mode") + } + + // Handle differencing. + var numDiffs int + var list textList + var keys []reflect.Value // invariant: len(list) == len(keys) + groups := coalesceAdjacentRecords(name, recs) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Handle equal records. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing records to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { + if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numLo++ + } + for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numHi++ + } + if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { + numHi++ // Avoid pointless coalescing of a single equal record + } + + // Format the equal values. + for _, r := range recs[:numLo] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + for _, r := range recs[numEqual-numHi : numEqual] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + recs = recs[numEqual:] + continue + } + + // Handle unequal records. + for _, r := range recs[:ds.NumDiff()] { + switch { + case opts.CanFormatDiffSlice(r.Value): + out := opts.FormatDiffSlice(r.Value) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + case r.Value.NumChildren == r.Value.MaxDepth: + outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i) + outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) + keys = append(keys, r.Key) + } + if outy != nil { + list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) + keys = append(keys, r.Key) + } + default: + out := opts.FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + } + recs = recs[ds.NumDiff():] + numDiffs += ds.NumDiff() + } + if maxGroup.IsZero() { + assert(len(recs) == 0) + } else { + list.AppendEllipsis(maxGroup) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + assert(len(list) == len(keys)) + + // For maps, the default formatting logic uses fmt.Stringer which may + // produce ambiguous output. Avoid calling String to disambiguate. + if k == reflect.Map { + var ambiguous bool + seenKeys := map[string]reflect.Value{} + for i, currKey := range keys { + if currKey.IsValid() { + strKey := list[i].Key + prevKey, seen := seenKeys[strKey] + if seen && prevKey.CanInterface() && currKey.CanInterface() { + ambiguous = prevKey.Interface() != currKey.Interface() + if ambiguous { + break + } + } + seenKeys[strKey] = currKey + } + } + if ambiguous { + for i, k := range keys { + if k.IsValid() { + list[i].Key = formatMapKey(k, true, ptrs) + } + } + } + } + + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} +} + +// coalesceAdjacentRecords coalesces the list of records into groups of +// adjacent equal, or unequal counts. +func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { + var prevCase int // Arbitrary index into which case last occurred + lastStats := func(i int) *diffStats { + if prevCase != i { + groups = append(groups, diffStats{Name: name}) + prevCase = i + } + return &groups[len(groups)-1] + } + for _, r := range recs { + switch rv := r.Value; { + case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: + lastStats(1).NumIgnored++ + case rv.NumDiff == 0: + lastStats(1).NumIdentical++ + case rv.NumDiff > 0 && !rv.ValueY.IsValid(): + lastStats(2).NumRemoved++ + case rv.NumDiff > 0 && !rv.ValueX.IsValid(): + lastStats(2).NumInserted++ + default: + lastStats(2).NumModified++ + } + } + return groups +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_references.go b/vendor/github.com/google/go-cmp/cmp/report_references.go new file mode 100644 index 00000000..be31b33a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_references.go @@ -0,0 +1,264 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/value" +) + +const ( + pointerDelimPrefix = "⟪" + pointerDelimSuffix = "⟫" +) + +// formatPointer prints the address of the pointer. +func formatPointer(p value.Pointer, withDelims bool) string { + v := p.Uintptr() + if flags.Deterministic { + v = 0xdeadf00f // Only used for stable testing purposes + } + if withDelims { + return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix + } + return formatHex(uint64(v)) +} + +// pointerReferences is a stack of pointers visited so far. +type pointerReferences [][2]value.Pointer + +func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { + if deref && vx.IsValid() { + vx = vx.Addr() + } + if deref && vy.IsValid() { + vy = vy.Addr() + } + switch d { + case diffUnknown, diffIdentical: + pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} + case diffRemoved: + pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} + case diffInserted: + pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} + } + *ps = append(*ps, pp) + return pp +} + +func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { + p = value.PointerOf(v) + for _, pp := range *ps { + if p == pp[0] || p == pp[1] { + return p, true + } + } + *ps = append(*ps, [2]value.Pointer{p, p}) + return p, false +} + +func (ps *pointerReferences) Pop() { + *ps = (*ps)[:len(*ps)-1] +} + +// trunkReferences is metadata for a textNode indicating that the sub-tree +// represents the value for either pointer in a pair of references. +type trunkReferences struct{ pp [2]value.Pointer } + +// trunkReference is metadata for a textNode indicating that the sub-tree +// represents the value for the given pointer reference. +type trunkReference struct{ p value.Pointer } + +// leafReference is metadata for a textNode indicating that the value is +// truncated as it refers to another part of the tree (i.e., a trunk). +type leafReference struct{ p value.Pointer } + +func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { + switch { + case pp[0].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} + case pp[1].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + case pp[0] == pp[1]: + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + default: + return &textWrap{Value: s, Metadata: trunkReferences{pp}} + } +} +func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} +} +func makeLeafReference(p value.Pointer, printAddress bool) textNode { + out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} +} + +// resolveReferences walks the textNode tree searching for any leaf reference +// metadata and resolves each against the corresponding trunk references. +// Since pointer addresses in memory are not particularly readable to the user, +// it replaces each pointer value with an arbitrary and unique reference ID. +func resolveReferences(s textNode) { + var walkNodes func(textNode, func(textNode)) + walkNodes = func(s textNode, f func(textNode)) { + f(s) + switch s := s.(type) { + case *textWrap: + walkNodes(s.Value, f) + case textList: + for _, r := range s { + walkNodes(r.Value, f) + } + } + } + + // Collect all trunks and leaves with reference metadata. + var trunks, leaves []*textWrap + walkNodes(s, func(s textNode) { + if s, ok := s.(*textWrap); ok { + switch s.Metadata.(type) { + case leafReference: + leaves = append(leaves, s) + case trunkReference, trunkReferences: + trunks = append(trunks, s) + } + } + }) + + // No leaf references to resolve. + if len(leaves) == 0 { + return + } + + // Collect the set of all leaf references to resolve. + leafPtrs := make(map[value.Pointer]bool) + for _, leaf := range leaves { + leafPtrs[leaf.Metadata.(leafReference).p] = true + } + + // Collect the set of trunk pointers that are always paired together. + // This allows us to assign a single ID to both pointers for brevity. + // If a pointer in a pair ever occurs by itself or as a different pair, + // then the pair is broken. + pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) + unpair := func(p value.Pointer) { + if !pairedTrunkPtrs[p].IsNil() { + pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half + } + pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + unpair(p.p) // standalone pointer cannot be part of a pair + case trunkReferences: + p0, ok0 := pairedTrunkPtrs[p.pp[0]] + p1, ok1 := pairedTrunkPtrs[p.pp[1]] + switch { + case !ok0 && !ok1: + // Register the newly seen pair. + pairedTrunkPtrs[p.pp[0]] = p.pp[1] + pairedTrunkPtrs[p.pp[1]] = p.pp[0] + case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: + // Exact pair already seen; do nothing. + default: + // Pair conflicts with some other pair; break all pairs. + unpair(p.pp[0]) + unpair(p.pp[1]) + } + } + } + + // Correlate each pointer referenced by leaves to a unique identifier, + // and print the IDs for each trunk that matches those pointers. + var nextID uint + ptrIDs := make(map[value.Pointer]uint) + newID := func() uint { + id := nextID + nextID++ + return id + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + if print := leafPtrs[p.p]; print { + id, ok := ptrIDs[p.p] + if !ok { + id = newID() + ptrIDs[p.p] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } + case trunkReferences: + print0 := leafPtrs[p.pp[0]] + print1 := leafPtrs[p.pp[1]] + if print0 || print1 { + id0, ok0 := ptrIDs[p.pp[0]] + id1, ok1 := ptrIDs[p.pp[1]] + isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] + if isPair { + var id uint + assert(ok0 == ok1) // must be seen together or not at all + if ok0 { + assert(id0 == id1) // must have the same ID + id = id0 + } else { + id = newID() + ptrIDs[p.pp[0]] = id + ptrIDs[p.pp[1]] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } else { + if print0 && !ok0 { + id0 = newID() + ptrIDs[p.pp[0]] = id0 + } + if print1 && !ok1 { + id1 = newID() + ptrIDs[p.pp[1]] = id1 + } + switch { + case print0 && print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) + case print0: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) + case print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) + } + } + } + } + } + + // Update all leaf references with the unique identifier. + for _, leaf := range leaves { + if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { + leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) + } + } +} + +func formatReference(id uint) string { + return fmt.Sprintf("ref#%d", id) +} + +func updateReferencePrefix(prefix, ref string) string { + if prefix == "" { + return pointerDelimPrefix + ref + pointerDelimSuffix + } + suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) + return pointerDelimPrefix + ref + ": " + suffix +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go new file mode 100644 index 00000000..2ab41fad --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -0,0 +1,414 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +var ( + anyType = reflect.TypeOf((*interface{})(nil)).Elem() + stringType = reflect.TypeOf((*string)(nil)).Elem() + bytesType = reflect.TypeOf((*[]byte)(nil)).Elem() + byteType = reflect.TypeOf((*byte)(nil)).Elem() +) + +type formatValueOptions struct { + // AvoidStringer controls whether to avoid calling custom stringer + // methods like error.Error or fmt.Stringer.String. + AvoidStringer bool + + // PrintAddresses controls whether to print the address of all pointers, + // slice elements, and maps. + PrintAddresses bool + + // QualifiedNames controls whether FormatType uses the fully qualified name + // (including the full package path as opposed to just the package name). + QualifiedNames bool + + // VerbosityLevel controls the amount of output to produce. + // A higher value produces more output. A value of zero or lower produces + // no output (represented using an ellipsis). + // If LimitVerbosity is false, then the level is treated as infinite. + VerbosityLevel int + + // LimitVerbosity specifies that formatting should respect VerbosityLevel. + LimitVerbosity bool +} + +// FormatType prints the type as if it were wrapping s. +// This may return s as-is depending on the current type and TypeMode mode. +func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { + // Check whether to emit the type or not. + switch opts.TypeMode { + case autoType: + switch t.Kind() { + case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: + if s.Equal(textNil) { + return s + } + default: + return s + } + if opts.DiffMode == diffIdentical { + return s // elide type for identical nodes + } + case elideType: + return s + } + + // Determine the type label, applying special handling for unnamed types. + typeName := value.TypeString(t, opts.QualifiedNames) + if t.Name() == "" { + // According to Go grammar, certain type literals contain symbols that + // do not strongly bind to the next lexicographical token (e.g., *T). + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Ptr: + typeName = "(" + typeName + ")" + } + } + return &textWrap{Prefix: typeName, Value: wrapParens(s)} +} + +// wrapParens wraps s with a set of parenthesis, but avoids it if the +// wrapped node itself is already surrounded by a pair of parenthesis or braces. +// It handles unwrapping one level of pointer-reference nodes. +func wrapParens(s textNode) textNode { + var refNode *textWrap + if s2, ok := s.(*textWrap); ok { + // Unwrap a single pointer reference node. + switch s2.Metadata.(type) { + case leafReference, trunkReference, trunkReferences: + refNode = s2 + if s3, ok := refNode.Value.(*textWrap); ok { + s2 = s3 + } + } + + // Already has delimiters that make parenthesis unnecessary. + hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") + hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") + if hasParens || hasBraces { + return s + } + } + if refNode != nil { + refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} + return s + } + return &textWrap{Prefix: "(", Value: s, Suffix: ")"} +} + +// FormatValue prints the reflect.Value, taking extra care to avoid descending +// into pointers already in ptrs. As pointers are visited, ptrs is also updated. +func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { + if !v.IsValid() { + return nil + } + t := v.Type() + + // Check slice element for cycles. + if parentKind == reflect.Slice { + ptrRef, visited := ptrs.Push(v.Addr()) + if visited { + return makeLeafReference(ptrRef, false) + } + defer ptrs.Pop() + defer func() { out = wrapTrunkReference(ptrRef, false, out) }() + } + + // Check whether there is an Error or String method to call. + if !opts.AvoidStringer && v.CanInterface() { + // Avoid calling Error or String methods on nil receivers since many + // implementations crash when doing so. + if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { + var prefix, strVal string + func() { + // Swallow and ignore any panics from String or Error. + defer func() { recover() }() + switch v := v.Interface().(type) { + case error: + strVal = v.Error() + prefix = "e" + case fmt.Stringer: + strVal = v.String() + prefix = "s" + } + }() + if prefix != "" { + return opts.formatString(prefix, strVal) + } + } + } + + // Check whether to explicitly wrap the result with the type. + var skipType bool + defer func() { + if !skipType { + out = opts.FormatType(t, out) + } + }() + + switch t.Kind() { + case reflect.Bool: + return textLine(fmt.Sprint(v.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return textLine(fmt.Sprint(v.Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uint8: + if parentKind == reflect.Slice || parentKind == reflect.Array { + return textLine(formatHex(v.Uint())) + } + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uintptr: + return textLine(formatHex(v.Uint())) + case reflect.Float32, reflect.Float64: + return textLine(fmt.Sprint(v.Float())) + case reflect.Complex64, reflect.Complex128: + return textLine(fmt.Sprint(v.Complex())) + case reflect.String: + return opts.formatString("", v.String()) + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + return textLine(formatPointer(value.PointerOf(v), true)) + case reflect.Struct: + var list textList + v := makeAddressable(v) // needed for retrieveUnexportedField + maxLen := v.NumField() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + for i := 0; i < v.NumField(); i++ { + vv := v.Field(i) + if vv.IsZero() { + continue // Elide fields with zero values + } + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sf := t.Field(i) + if supportExporters && !isExported(sf.Name) { + vv = retrieveUnexportedField(v, sf, true) + } + s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) + list = append(list, textRecord{Key: sf.Name, Value: s}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case reflect.Slice: + if v.IsNil() { + return textNil + } + + // Check whether this is a []byte of text data. + if t.Elem() == byteType { + b := v.Bytes() + isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) } + if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { + out = opts.formatString("", string(b)) + skipType = true + return opts.FormatType(t, out) + } + } + + fallthrough + case reflect.Array: + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for i := 0; i < v.Len(); i++ { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) + list = append(list, textRecord{Value: s}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if t.Kind() == reflect.Slice && opts.PrintAddresses { + header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) + out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} + } + return out + case reflect.Map: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + return makeLeafReference(ptrRef, opts.PrintAddresses) + } + defer ptrs.Pop() + + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for _, k := range value.SortKeys(v.MapKeys()) { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sk := formatMapKey(k, false, ptrs) + sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) + list = append(list, textRecord{Key: sk, Value: sv}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + return out + case reflect.Ptr: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + out = makeLeafReference(ptrRef, opts.PrintAddresses) + return &textWrap{Prefix: "&", Value: out} + } + defer ptrs.Pop() + + // Skip the name only if this is an unnamed pointer type. + // Otherwise taking the address of a value does not reproduce + // the named pointer type. + if v.Type().Name() == "" { + skipType = true // Let the underlying value print the type instead + } + out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + out = &textWrap{Prefix: "&", Value: out} + return out + case reflect.Interface: + if v.IsNil() { + return textNil + } + // Interfaces accept different concrete types, + // so configure the underlying value to explicitly print the type. + return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) + default: + panic(fmt.Sprintf("%v kind not handled", v.Kind())) + } +} + +func (opts formatOptions) formatString(prefix, s string) textNode { + maxLen := len(s) + maxLines := strings.Count(s, "\n") + 1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... + maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + } + + // For multiline strings, use the triple-quote syntax, + // but only use it when printing removed or inserted nodes since + // we only want the extra verbosity for those cases. + lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n") + isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+') + for i := 0; i < len(lines) && isTripleQuoted; i++ { + lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + line := lines[i] + isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen + } + if isTripleQuoted { + var list textList + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + for i, line := range lines { + if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 { + comment := commentString(fmt.Sprintf("%d elided lines", numElided)) + list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment}) + break + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true}) + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + return &textWrap{Prefix: "(", Value: list, Suffix: ")"} + } + + // Format the string as a single-line quoted string. + if len(s) > maxLen+len(textEllipsis) { + return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis)) + } + return textLine(prefix + formatString(s)) +} + +// formatMapKey formats v as if it were a map key. +// The result is guaranteed to be a single line. +func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { + var opts formatOptions + opts.DiffMode = diffIdentical + opts.TypeMode = elideType + opts.PrintAddresses = disambiguate + opts.AvoidStringer = disambiguate + opts.QualifiedNames = disambiguate + opts.VerbosityLevel = maxVerbosityPreset + opts.LimitVerbosity = true + s := opts.FormatValue(v, reflect.Map, ptrs).String() + return strings.TrimSpace(s) +} + +// formatString prints s as a double-quoted or backtick-quoted string. +func formatString(s string) string { + // Use quoted string if it the same length as a raw string literal. + // Otherwise, attempt to use the raw string form. + qs := strconv.Quote(s) + if len(qs) == 1+len(s)+1 { + return qs + } + + // Disallow newlines to ensure output is a single line. + // Only allow printable runes for readability purposes. + rawInvalid := func(r rune) bool { + return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') + } + if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { + return "`" + s + "`" + } + return qs +} + +// formatHex prints u as a hexadecimal integer in Go notation. +func formatHex(u uint64) string { + var f string + switch { + case u <= 0xff: + f = "0x%02x" + case u <= 0xffff: + f = "0x%04x" + case u <= 0xffffff: + f = "0x%06x" + case u <= 0xffffffff: + f = "0x%08x" + case u <= 0xffffffffff: + f = "0x%010x" + case u <= 0xffffffffffff: + f = "0x%012x" + case u <= 0xffffffffffffff: + f = "0x%014x" + case u <= 0xffffffffffffffff: + f = "0x%016x" + } + return fmt.Sprintf(f, u) +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go new file mode 100644 index 00000000..23e444f6 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go @@ -0,0 +1,614 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "math" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/diff" +) + +// CanFormatDiffSlice reports whether we support custom formatting for nodes +// that are slices of primitive kinds or strings. +func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { + switch { + case opts.DiffMode != diffUnknown: + return false // Must be formatting in diff mode + case v.NumDiff == 0: + return false // No differences detected + case !v.ValueX.IsValid() || !v.ValueY.IsValid(): + return false // Both values must be valid + case v.NumIgnored > 0: + return false // Some ignore option was used + case v.NumTransformed > 0: + return false // Some transform option was used + case v.NumCompared > 1: + return false // More than one comparison was used + case v.NumCompared == 1 && v.Type.Name() != "": + // The need for cmp to check applicability of options on every element + // in a slice is a significant performance detriment for large []byte. + // The workaround is to specify Comparer(bytes.Equal), + // which enables cmp to compare []byte more efficiently. + // If they differ, we still want to provide batched diffing. + // The logic disallows named types since they tend to have their own + // String method, with nicer formatting than what this provides. + return false + } + + // Check whether this is an interface with the same concrete types. + t := v.Type + vx, vy := v.ValueX, v.ValueY + if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() + } + + // Check whether we provide specialized diffing for this type. + switch t.Kind() { + case reflect.String: + case reflect.Array, reflect.Slice: + // Only slices of primitive types have specialized handling. + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + default: + return false + } + + // Both slice values have to be non-empty. + if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) { + return false + } + + // If a sufficient number of elements already differ, + // use specialized formatting even if length requirement is not met. + if v.NumDiff > v.NumSame { + return true + } + default: + return false + } + + // Use specialized string diffing for longer slices or strings. + const minLength = 32 + return vx.Len() >= minLength && vy.Len() >= minLength +} + +// FormatDiffSlice prints a diff for the slices (or strings) represented by v. +// This provides custom-tailored logic to make printing of differences in +// textual strings and slices of primitive kinds more readable. +func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { + assert(opts.DiffMode == diffUnknown) + t, vx, vy := v.Type, v.ValueX, v.ValueY + if t.Kind() == reflect.Interface { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() + opts = opts.WithTypeMode(emitType) + } + + // Auto-detect the type of the data. + var sx, sy string + var ssx, ssy []string + var isString, isMostlyText, isPureLinedText, isBinary bool + switch { + case t.Kind() == reflect.String: + sx, sy = vx.String(), vy.String() + isString = true + case t.Kind() == reflect.Slice && t.Elem() == byteType: + sx, sy = string(vx.Bytes()), string(vy.Bytes()) + isString = true + case t.Kind() == reflect.Array: + // Arrays need to be addressable for slice operations to work. + vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() + vx2.Set(vx) + vy2.Set(vy) + vx, vy = vx2, vy2 + } + if isString { + var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int + for i, r := range sx + sy { + numTotalRunes++ + if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError { + numValidRunes++ + } + if r == '\n' { + if maxLineLen < i-lastLineIdx { + maxLineLen = i - lastLineIdx + } + lastLineIdx = i + 1 + numLines++ + } + } + isPureText := numValidRunes == numTotalRunes + isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes)) + isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024 + isBinary = !isMostlyText + + // Avoid diffing by lines if it produces a significantly more complex + // edit script than diffing by bytes. + if isPureLinedText { + ssx = strings.Split(sx, "\n") + ssy = strings.Split(sy, "\n") + esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result { + return diff.BoolResult(ssx[ix] == ssy[iy]) + }) + esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result { + return diff.BoolResult(sx[ix] == sy[iy]) + }) + efficiencyLines := float64(esLines.Dist()) / float64(len(esLines)) + efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes)) + quotedLength := len(strconv.Quote(sx + sy)) + unquotedLength := len(sx) + len(sy) + escapeExpansionRatio := float64(quotedLength) / float64(unquotedLength) + isPureLinedText = efficiencyLines < 4*efficiencyBytes || escapeExpansionRatio > 1.1 + } + } + + // Format the string into printable records. + var list textList + var delim string + switch { + // If the text appears to be multi-lined text, + // then perform differencing across individual lines. + case isPureLinedText: + list = opts.formatDiffSlice( + reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.Index(0).String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + delim = "\n" + + // If possible, use a custom triple-quote (""") syntax for printing + // differences in a string literal. This format is more readable, + // but has edge-cases where differences are visually indistinguishable. + // This format is avoided under the following conditions: + // - A line starts with `"""` + // - A line starts with "..." + // - A line contains non-printable characters + // - Adjacent different lines differ only by whitespace + // + // For example: + // + // """ + // ... // 3 identical lines + // foo + // bar + // - baz + // + BAZ + // """ + isTripleQuoted := true + prevRemoveLines := map[string]bool{} + prevInsertLines := map[string]bool{} + var list2 textList + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + for _, r := range list { + if !r.Value.Equal(textEllipsis) { + line, _ := strconv.Unquote(string(r.Value.(textLine))) + line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + normLine := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 // drop whitespace to avoid visually indistinguishable output + } + return r + }, line) + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" + switch r.Diff { + case diffRemoved: + isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] + prevRemoveLines[normLine] = true + case diffInserted: + isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] + prevInsertLines[normLine] = true + } + if !isTripleQuoted { + break + } + r.Value = textLine(line) + r.ElideComma = true + } + if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group + prevRemoveLines = map[string]bool{} + prevInsertLines = map[string]bool{} + } + list2 = append(list2, r) + } + if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { + list2 = list2[:len(list2)-1] // elide single empty line at the end + } + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + if isTripleQuoted { + var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} + switch t.Kind() { + case reflect.String: + if t != stringType { + out = opts.FormatType(t, out) + } + case reflect.Slice: + // Always emit type for slices since the triple-quote syntax + // looks like a string (not a slice). + opts = opts.WithTypeMode(emitType) + out = opts.FormatType(t, out) + } + return out + } + + // If the text appears to be single-lined text, + // then perform differencing in approximately fixed-sized chunks. + // The output is printed as quoted strings. + case isMostlyText: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + + // If the text appears to be binary data, + // then perform differencing in approximately fixed-sized chunks. + // The output is inspired by hexdump. + case isBinary: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + ss = append(ss, formatHex(v.Index(i).Uint())) + } + s := strings.Join(ss, ", ") + comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) + return textRecord{Diff: d, Value: textLine(s), Comment: comment} + }, + ) + + // For all other slices of primitive types, + // then perform differencing in approximately fixed-sized chunks. + // The size of each chunk depends on the width of the element kind. + default: + var chunkSize int + if t.Elem().Kind() == reflect.Bool { + chunkSize = 16 + } else { + switch t.Elem().Bits() { + case 8: + chunkSize = 16 + case 16: + chunkSize = 12 + case 32: + chunkSize = 8 + default: + chunkSize = 8 + } + } + list = opts.formatDiffSlice( + vx, vy, chunkSize, t.Elem().Kind().String(), + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + ss = append(ss, fmt.Sprint(v.Index(i).Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + ss = append(ss, fmt.Sprint(v.Index(i).Uint())) + case reflect.Uint8, reflect.Uintptr: + ss = append(ss, formatHex(v.Index(i).Uint())) + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + ss = append(ss, fmt.Sprint(v.Index(i).Interface())) + } + } + s := strings.Join(ss, ", ") + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + } + + // Wrap the output with appropriate type information. + var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if !isMostlyText { + // The "{...}" byte-sequence literal is not valid Go syntax for strings. + // Emit the type for extra clarity (e.g. "string{...}"). + if t.Kind() == reflect.String { + opts = opts.WithTypeMode(emitType) + } + return opts.FormatType(t, out) + } + switch t.Kind() { + case reflect.String: + out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != stringType { + out = opts.FormatType(t, out) + } + case reflect.Slice: + out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != bytesType { + out = opts.FormatType(t, out) + } + } + return out +} + +// formatASCII formats s as an ASCII string. +// This is useful for printing binary strings in a semi-legible way. +func formatASCII(s string) string { + b := bytes.Repeat([]byte{'.'}, len(s)) + for i := 0; i < len(s); i++ { + if ' ' <= s[i] && s[i] <= '~' { + b[i] = s[i] + } + } + return string(b) +} + +func (opts formatOptions) formatDiffSlice( + vx, vy reflect.Value, chunkSize int, name string, + makeRec func(reflect.Value, diffMode) textRecord, +) (list textList) { + eq := func(ix, iy int) bool { + return vx.Index(ix).Interface() == vy.Index(iy).Interface() + } + es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { + return diff.BoolResult(eq(ix, iy)) + }) + + appendChunks := func(v reflect.Value, d diffMode) int { + n0 := v.Len() + for v.Len() > 0 { + n := chunkSize + if n > v.Len() { + n = v.Len() + } + list = append(list, makeRec(v.Slice(0, n), d)) + v = v.Slice(n, v.Len()) + } + return n0 - v.Len() + } + + var numDiffs int + maxLen := -1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + opts.VerbosityLevel-- + } + + groups := coalesceAdjacentEdits(name, es) + groups = coalesceInterveningIdentical(groups, chunkSize/4) + groups = cleanupSurroundingIdentical(groups, eq) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Print equal. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing equal bytes to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { + numLo++ + } + for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + numHi++ + } + if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { + numHi = numEqual - numLo // Avoid pointless coalescing of single equal row + } + + // Print the equal bytes. + appendChunks(vx.Slice(0, numLo), diffIdentical) + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + } + appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) + vx = vx.Slice(numEqual, vx.Len()) + vy = vy.Slice(numEqual, vy.Len()) + continue + } + + // Print unequal. + len0 := len(list) + nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) + vx = vx.Slice(nx, vx.Len()) + ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) + vy = vy.Slice(ny, vy.Len()) + numDiffs += len(list) - len0 + } + if maxGroup.IsZero() { + assert(vx.Len() == 0 && vy.Len() == 0) + } else { + list.AppendEllipsis(maxGroup) + } + return list +} + +// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent +// equal or unequal counts. +// +// Example: +// +// Input: "..XXY...Y" +// Output: [ +// {NumIdentical: 2}, +// {NumRemoved: 2, NumInserted 1}, +// {NumIdentical: 3}, +// {NumInserted: 1}, +// ] +func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { + var prevMode byte + lastStats := func(mode byte) *diffStats { + if prevMode != mode { + groups = append(groups, diffStats{Name: name}) + prevMode = mode + } + return &groups[len(groups)-1] + } + for _, e := range es { + switch e { + case diff.Identity: + lastStats('=').NumIdentical++ + case diff.UniqueX: + lastStats('!').NumRemoved++ + case diff.UniqueY: + lastStats('!').NumInserted++ + case diff.Modified: + lastStats('!').NumModified++ + } + } + return groups +} + +// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) +// equal groups into adjacent unequal groups that currently result in a +// dual inserted/removed printout. This acts as a high-pass filter to smooth +// out high-frequency changes within the windowSize. +// +// Example: +// +// WindowSize: 16, +// Input: [ +// {NumIdentical: 61}, // group 0 +// {NumRemoved: 3, NumInserted: 1}, // group 1 +// {NumIdentical: 6}, // ├── coalesce +// {NumInserted: 2}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 9}, // └── coalesce +// {NumIdentical: 64}, // group 2 +// {NumRemoved: 3, NumInserted: 1}, // group 3 +// {NumIdentical: 6}, // ├── coalesce +// {NumInserted: 2}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 7}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 2}, // └── coalesce +// {NumIdentical: 63}, // group 4 +// ] +// Output: [ +// {NumIdentical: 61}, +// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, +// {NumIdentical: 64}, +// {NumIdentical: 8, NumRemoved: 12, NumInserted: 3}, +// {NumIdentical: 63}, +// ] +func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { + groups, groupsOrig := groups[:0], groups + for i, ds := range groupsOrig { + if len(groups) >= 2 && ds.NumDiff() > 0 { + prev := &groups[len(groups)-2] // Unequal group + curr := &groups[len(groups)-1] // Equal group + next := &groupsOrig[i] // Unequal group + hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 + hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 + if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { + *prev = prev.Append(*curr).Append(*next) + groups = groups[:len(groups)-1] // Truncate off equal group + continue + } + } + groups = append(groups, ds) + } + return groups +} + +// cleanupSurroundingIdentical scans through all unequal groups, and +// moves any leading sequence of equal elements to the preceding equal group and +// moves and trailing sequence of equal elements to the succeeding equal group. +// +// This is necessary since coalesceInterveningIdentical may coalesce edit groups +// together such that leading/trailing spans of equal elements becomes possible. +// Note that this can occur even with an optimal diffing algorithm. +// +// Example: +// +// Input: [ +// {NumIdentical: 61}, +// {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements +// {NumIdentical: 67}, +// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements +// {NumIdentical: 54}, +// ] +// Output: [ +// {NumIdentical: 64}, // incremented by 3 +// {NumRemoved: 9}, +// {NumIdentical: 67}, +// {NumRemoved: 9}, +// {NumIdentical: 64}, // incremented by 10 +// ] +func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats { + var ix, iy int // indexes into sequence x and y + for i, ds := range groups { + // Handle equal group. + if ds.NumDiff() == 0 { + ix += ds.NumIdentical + iy += ds.NumIdentical + continue + } + + // Handle unequal group. + nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified + ny := ds.NumIdentical + ds.NumInserted + ds.NumModified + var numLeadingIdentical, numTrailingIdentical int + for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ { + numLeadingIdentical++ + } + for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ { + numTrailingIdentical++ + } + if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 { + if numLeadingIdentical > 0 { + // Remove leading identical span from this group and + // insert it into the preceding group. + if i-1 >= 0 { + groups[i-1].NumIdentical += numLeadingIdentical + } else { + // No preceding group exists, so prepend a new group, + // but do so after we finish iterating over all groups. + defer func() { + groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...) + }() + } + // Increment indexes since the preceding group would have handled this. + ix += numLeadingIdentical + iy += numLeadingIdentical + } + if numTrailingIdentical > 0 { + // Remove trailing identical span from this group and + // insert it into the succeeding group. + if i+1 < len(groups) { + groups[i+1].NumIdentical += numTrailingIdentical + } else { + // No succeeding group exists, so append a new group, + // but do so after we finish iterating over all groups. + defer func() { + groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical}) + }() + } + // Do not increment indexes since the succeeding group will handle this. + } + + // Update this group since some identical elements were removed. + nx -= numIdentical + ny -= numIdentical + groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny} + } + ix += nx + iy += ny + } + return groups +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go new file mode 100644 index 00000000..388fcf57 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_text.go @@ -0,0 +1,432 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "math/rand" + "strings" + "time" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + +const maxColumnLength = 80 + +type indentMode int + +func (n indentMode) appendIndent(b []byte, d diffMode) []byte { + // The output of Diff is documented as being unstable to provide future + // flexibility in changing the output for more humanly readable reports. + // This logic intentionally introduces instability to the exact output + // so that users can detect accidental reliance on stability early on, + // rather than much later when an actual change to the format occurs. + if flags.Deterministic || randBool { + // Use regular spaces (U+0020). + switch d { + case diffUnknown, diffIdentical: + b = append(b, " "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } else { + // Use non-breaking spaces (U+00a0). + switch d { + case diffUnknown, diffIdentical: + b = append(b, "  "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } + return repeatCount(n).appendChar(b, '\t') +} + +type repeatCount int + +func (n repeatCount) appendChar(b []byte, c byte) []byte { + for ; n > 0; n-- { + b = append(b, c) + } + return b +} + +// textNode is a simplified tree-based representation of structured text. +// Possible node types are textWrap, textList, or textLine. +type textNode interface { + // Len reports the length in bytes of a single-line version of the tree. + // Nested textRecord.Diff and textRecord.Comment fields are ignored. + Len() int + // Equal reports whether the two trees are structurally identical. + // Nested textRecord.Diff and textRecord.Comment fields are compared. + Equal(textNode) bool + // String returns the string representation of the text tree. + // It is not guaranteed that len(x.String()) == x.Len(), + // nor that x.String() == y.String() implies that x.Equal(y). + String() string + + // formatCompactTo formats the contents of the tree as a single-line string + // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment + // fields are ignored. + // + // However, not all nodes in the tree should be collapsed as a single-line. + // If a node can be collapsed as a single-line, it is replaced by a textLine + // node. Since the top-level node cannot replace itself, this also returns + // the current node itself. + // + // This does not mutate the receiver. + formatCompactTo([]byte, diffMode) ([]byte, textNode) + // formatExpandedTo formats the contents of the tree as a multi-line string + // to the provided buffer. In order for column alignment to operate well, + // formatCompactTo must be called before calling formatExpandedTo. + formatExpandedTo([]byte, diffMode, indentMode) []byte +} + +// textWrap is a wrapper that concatenates a prefix and/or a suffix +// to the underlying node. +type textWrap struct { + Prefix string // e.g., "bytes.Buffer{" + Value textNode // textWrap | textList | textLine + Suffix string // e.g., "}" + Metadata interface{} // arbitrary metadata; has no effect on formatting +} + +func (s *textWrap) Len() int { + return len(s.Prefix) + s.Value.Len() + len(s.Suffix) +} +func (s1 *textWrap) Equal(s2 textNode) bool { + if s2, ok := s2.(*textWrap); ok { + return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix + } + return false +} +func (s *textWrap) String() string { + var d diffMode + var n indentMode + _, s2 := s.formatCompactTo(nil, d) + b := n.appendIndent(nil, d) // Leading indent + b = s2.formatExpandedTo(b, d, n) // Main body + b = append(b, '\n') // Trailing newline + return string(b) +} +func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + n0 := len(b) // Original buffer length + b = append(b, s.Prefix...) + b, s.Value = s.Value.formatCompactTo(b, d) + b = append(b, s.Suffix...) + if _, ok := s.Value.(textLine); ok { + return b, textLine(b[n0:]) + } + return b, s +} +func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + b = append(b, s.Prefix...) + b = s.Value.formatExpandedTo(b, d, n) + b = append(b, s.Suffix...) + return b +} + +// textList is a comma-separated list of textWrap or textLine nodes. +// The list may be formatted as multi-lines or single-line at the discretion +// of the textList.formatCompactTo method. +type textList []textRecord +type textRecord struct { + Diff diffMode // e.g., 0 or '-' or '+' + Key string // e.g., "MyField" + Value textNode // textWrap | textLine + ElideComma bool // avoid trailing comma + Comment fmt.Stringer // e.g., "6 identical fields" +} + +// AppendEllipsis appends a new ellipsis node to the list if none already +// exists at the end. If cs is non-zero it coalesces the statistics with the +// previous diffStats. +func (s *textList) AppendEllipsis(ds diffStats) { + hasStats := !ds.IsZero() + if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { + if hasStats { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) + } else { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) + } + return + } + if hasStats { + (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) + } +} + +func (s textList) Len() (n int) { + for i, r := range s { + n += len(r.Key) + if r.Key != "" { + n += len(": ") + } + n += r.Value.Len() + if i < len(s)-1 { + n += len(", ") + } + } + return n +} + +func (s1 textList) Equal(s2 textNode) bool { + if s2, ok := s2.(textList); ok { + if len(s1) != len(s2) { + return false + } + for i := range s1 { + r1, r2 := s1[i], s2[i] + if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { + return false + } + } + return true + } + return false +} + +func (s textList) String() string { + return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() +} + +func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + s = append(textList(nil), s...) // Avoid mutating original + + // Determine whether we can collapse this list as a single line. + n0 := len(b) // Original buffer length + var multiLine bool + for i, r := range s { + if r.Diff == diffInserted || r.Diff == diffRemoved { + multiLine = true + } + b = append(b, r.Key...) + if r.Key != "" { + b = append(b, ": "...) + } + b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) + if _, ok := s[i].Value.(textLine); !ok { + multiLine = true + } + if r.Comment != nil { + multiLine = true + } + if i < len(s)-1 { + b = append(b, ", "...) + } + } + // Force multi-lined output when printing a removed/inserted node that + // is sufficiently long. + if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { + multiLine = true + } + if !multiLine { + return b, textLine(b[n0:]) + } + return b, s +} + +func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + alignKeyLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return r.Key == "" || !isLine + }, + func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, + ) + alignValueLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil + }, + func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, + ) + + // Format lists of simple lists in a batched form. + // If the list is sequence of only textLine values, + // then batch multiple values on a single line. + var isSimple bool + for _, r := range s { + _, isLine := r.Value.(textLine) + isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil + if !isSimple { + break + } + } + if isSimple { + n++ + var batch []byte + emitBatch := func() { + if len(batch) > 0 { + b = n.appendIndent(append(b, '\n'), d) + b = append(b, bytes.TrimRight(batch, " ")...) + batch = batch[:0] + } + } + for _, r := range s { + line := r.Value.(textLine) + if len(batch)+len(line)+len(", ") > maxColumnLength { + emitBatch() + } + batch = append(batch, line...) + batch = append(batch, ", "...) + } + emitBatch() + n-- + return n.appendIndent(append(b, '\n'), d) + } + + // Format the list as a multi-lined output. + n++ + for i, r := range s { + b = n.appendIndent(append(b, '\n'), d|r.Diff) + if r.Key != "" { + b = append(b, r.Key+": "...) + } + b = alignKeyLens[i].appendChar(b, ' ') + + b = r.Value.formatExpandedTo(b, d|r.Diff, n) + if !r.ElideComma { + b = append(b, ',') + } + b = alignValueLens[i].appendChar(b, ' ') + + if r.Comment != nil { + b = append(b, " // "+r.Comment.String()...) + } + } + n-- + + return n.appendIndent(append(b, '\n'), d) +} + +func (s textList) alignLens( + skipFunc func(textRecord) bool, + lenFunc func(textRecord) int, +) []repeatCount { + var startIdx, endIdx, maxLen int + lens := make([]repeatCount, len(s)) + for i, r := range s { + if skipFunc(r) { + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + startIdx, endIdx, maxLen = i+1, i+1, 0 + } else { + if maxLen < lenFunc(r) { + maxLen = lenFunc(r) + } + endIdx = i + 1 + } + } + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + return lens +} + +// textLine is a single-line segment of text and is always a leaf node +// in the textNode tree. +type textLine []byte + +var ( + textNil = textLine("nil") + textEllipsis = textLine("...") +) + +func (s textLine) Len() int { + return len(s) +} +func (s1 textLine) Equal(s2 textNode) bool { + if s2, ok := s2.(textLine); ok { + return bytes.Equal([]byte(s1), []byte(s2)) + } + return false +} +func (s textLine) String() string { + return string(s) +} +func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + return append(b, s...), s +} +func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { + return append(b, s...) +} + +type diffStats struct { + Name string + NumIgnored int + NumIdentical int + NumRemoved int + NumInserted int + NumModified int +} + +func (s diffStats) IsZero() bool { + s.Name = "" + return s == diffStats{} +} + +func (s diffStats) NumDiff() int { + return s.NumRemoved + s.NumInserted + s.NumModified +} + +func (s diffStats) Append(ds diffStats) diffStats { + assert(s.Name == ds.Name) + s.NumIgnored += ds.NumIgnored + s.NumIdentical += ds.NumIdentical + s.NumRemoved += ds.NumRemoved + s.NumInserted += ds.NumInserted + s.NumModified += ds.NumModified + return s +} + +// String prints a humanly-readable summary of coalesced records. +// +// Example: +// +// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" +func (s diffStats) String() string { + var ss []string + var sum int + labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} + counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} + for i, n := range counts { + if n > 0 { + ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) + } + sum += n + } + + // Pluralize the name (adjusting for some obscure English grammar rules). + name := s.Name + if sum > 1 { + name += "s" + if strings.HasSuffix(name, "ys") { + name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" + } + } + + // Format the list according to English grammar (with Oxford comma). + switch n := len(ss); n { + case 0: + return "" + case 1, 2: + return strings.Join(ss, " and ") + " " + name + default: + return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name + } +} + +type commentString string + +func (s commentString) String() string { return string(s) } diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go new file mode 100644 index 00000000..668d470f --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_value.go @@ -0,0 +1,121 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import "reflect" + +// valueNode represents a single node within a report, which is a +// structured representation of the value tree, containing information +// regarding which nodes are equal or not. +type valueNode struct { + parent *valueNode + + Type reflect.Type + ValueX reflect.Value + ValueY reflect.Value + + // NumSame is the number of leaf nodes that are equal. + // All descendants are equal only if NumDiff is 0. + NumSame int + // NumDiff is the number of leaf nodes that are not equal. + NumDiff int + // NumIgnored is the number of leaf nodes that are ignored. + NumIgnored int + // NumCompared is the number of leaf nodes that were compared + // using an Equal method or Comparer function. + NumCompared int + // NumTransformed is the number of non-leaf nodes that were transformed. + NumTransformed int + // NumChildren is the number of transitive descendants of this node. + // This counts from zero; thus, leaf nodes have no descendants. + NumChildren int + // MaxDepth is the maximum depth of the tree. This counts from zero; + // thus, leaf nodes have a depth of zero. + MaxDepth int + + // Records is a list of struct fields, slice elements, or map entries. + Records []reportRecord // If populated, implies Value is not populated + + // Value is the result of a transformation, pointer indirect, of + // type assertion. + Value *valueNode // If populated, implies Records is not populated + + // TransformerName is the name of the transformer. + TransformerName string // If non-empty, implies Value is populated +} +type reportRecord struct { + Key reflect.Value // Invalid for slice element + Value *valueNode +} + +func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { + vx, vy := ps.Values() + child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} + switch s := ps.(type) { + case StructField: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) + case SliceIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Value: child}) + case MapIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) + case Indirect: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case TypeAssertion: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case Transform: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + parent.TransformerName = s.Name() + parent.NumTransformed++ + default: + assert(parent == nil) // Must be the root step + } + return child +} + +func (r *valueNode) Report(rs Result) { + assert(r.MaxDepth == 0) // May only be called on leaf nodes + + if rs.ByIgnore() { + r.NumIgnored++ + } else { + if rs.Equal() { + r.NumSame++ + } else { + r.NumDiff++ + } + } + assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) + + if rs.ByMethod() { + r.NumCompared++ + } + if rs.ByFunc() { + r.NumCompared++ + } + assert(r.NumCompared <= 1) +} + +func (child *valueNode) PopStep() (parent *valueNode) { + if child.parent == nil { + return nil + } + parent = child.parent + parent.NumSame += child.NumSame + parent.NumDiff += child.NumDiff + parent.NumIgnored += child.NumIgnored + parent.NumCompared += child.NumCompared + parent.NumTransformed += child.NumTransformed + parent.NumChildren += child.NumChildren + 1 + if parent.MaxDepth < child.MaxDepth+1 { + parent.MaxDepth = child.MaxDepth + 1 + } + return parent +} diff --git a/vendor/github.com/json-iterator/go/README.md b/vendor/github.com/json-iterator/go/README.md index 50d56ffb..c589addf 100644 --- a/vendor/github.com/json-iterator/go/README.md +++ b/vendor/github.com/json-iterator/go/README.md @@ -1,5 +1,5 @@ [![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) -[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/json-iterator/go) +[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) [![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) [![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) [![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) @@ -8,8 +8,6 @@ A high-performance 100% compatible drop-in replacement of "encoding/json" -You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go) - # Benchmark ![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) @@ -18,16 +16,16 @@ Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/githu Raw Result (easyjson requires static code generation) -| | ns/op | allocation bytes | allocation times | -| --- | --- | --- | --- | -| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | -| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | -| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | -| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | -| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | -| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | +| | ns/op | allocation bytes | allocation times | +| --------------- | ----------- | ---------------- | ---------------- | +| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | +| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | +| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | +| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | +| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | +| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | -Always benchmark with your own workload. +Always benchmark with your own workload. The result depends heavily on the data input. # Usage @@ -41,10 +39,10 @@ import "encoding/json" json.Marshal(&data) ``` -with +with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Marshal(&data) @@ -60,7 +58,7 @@ json.Unmarshal(input, &data) with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Unmarshal(input, &data) @@ -78,10 +76,10 @@ go get github.com/json-iterator/go Contributors -* [thockin](https://github.com/thockin) -* [mattn](https://github.com/mattn) -* [cch123](https://github.com/cch123) -* [Oleg Shaldybin](https://github.com/olegshaldybin) -* [Jason Toffaletti](https://github.com/toffaletti) +- [thockin](https://github.com/thockin) +- [mattn](https://github.com/mattn) +- [cch123](https://github.com/cch123) +- [Oleg Shaldybin](https://github.com/olegshaldybin) +- [Jason Toffaletti](https://github.com/toffaletti) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) diff --git a/vendor/github.com/json-iterator/go/adapter.go b/vendor/github.com/json-iterator/go/adapter.go index e674d0f3..92d2cc4a 100644 --- a/vendor/github.com/json-iterator/go/adapter.go +++ b/vendor/github.com/json-iterator/go/adapter.go @@ -16,7 +16,7 @@ func Unmarshal(data []byte, v interface{}) error { return ConfigDefault.Unmarshal(data, v) } -// UnmarshalFromString convenient method to read from string instead of []byte +// UnmarshalFromString is a convenient method to read from string instead of []byte func UnmarshalFromString(str string, v interface{}) error { return ConfigDefault.UnmarshalFromString(str, v) } diff --git a/vendor/github.com/json-iterator/go/any_str.go b/vendor/github.com/json-iterator/go/any_str.go index a4b93c78..1f12f661 100644 --- a/vendor/github.com/json-iterator/go/any_str.go +++ b/vendor/github.com/json-iterator/go/any_str.go @@ -64,7 +64,6 @@ func (any *stringAny) ToInt64() int64 { flag := 1 startPos := 0 - endPos := 0 if any.val[0] == '+' || any.val[0] == '-' { startPos = 1 } @@ -73,6 +72,7 @@ func (any *stringAny) ToInt64() int64 { flag = -1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 @@ -98,7 +98,6 @@ func (any *stringAny) ToUint64() uint64 { } startPos := 0 - endPos := 0 if any.val[0] == '-' { return 0 @@ -107,6 +106,7 @@ func (any *stringAny) ToUint64() uint64 { startPos = 1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 diff --git a/vendor/github.com/json-iterator/go/config.go b/vendor/github.com/json-iterator/go/config.go index 8c58fcba..2adcdc3b 100644 --- a/vendor/github.com/json-iterator/go/config.go +++ b/vendor/github.com/json-iterator/go/config.go @@ -183,11 +183,11 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) { encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { rawMessage := *(*json.RawMessage)(ptr) iter := cfg.BorrowIterator([]byte(rawMessage)) + defer cfg.ReturnIterator(iter) iter.Read() - if iter.Error != nil { + if iter.Error != nil && iter.Error != io.EOF { stream.WriteRaw("null") } else { - cfg.ReturnIterator(iter) stream.WriteRaw(string(rawMessage)) } }, func(ptr unsafe.Pointer) bool { diff --git a/vendor/github.com/json-iterator/go/go.mod b/vendor/github.com/json-iterator/go/go.mod new file mode 100644 index 00000000..e817cccb --- /dev/null +++ b/vendor/github.com/json-iterator/go/go.mod @@ -0,0 +1,11 @@ +module github.com/json-iterator/go + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/google/gofuzz v1.0.0 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 + github.com/modern-go/reflect2 v1.0.2 + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/json-iterator/go/go.sum b/vendor/github.com/json-iterator/go/go.sum new file mode 100644 index 00000000..4b7bb8a2 --- /dev/null +++ b/vendor/github.com/json-iterator/go/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/json-iterator/go/iter.go b/vendor/github.com/json-iterator/go/iter.go index 95ae54fb..29b31cf7 100644 --- a/vendor/github.com/json-iterator/go/iter.go +++ b/vendor/github.com/json-iterator/go/iter.go @@ -74,6 +74,7 @@ type Iterator struct { buf []byte head int tail int + depth int captureStartedAt int captured []byte Error error @@ -88,6 +89,7 @@ func NewIterator(cfg API) *Iterator { buf: nil, head: 0, tail: 0, + depth: 0, } } @@ -99,6 +101,7 @@ func Parse(cfg API, reader io.Reader, bufSize int) *Iterator { buf: make([]byte, bufSize), head: 0, tail: 0, + depth: 0, } } @@ -110,6 +113,7 @@ func ParseBytes(cfg API, input []byte) *Iterator { buf: input, head: 0, tail: len(input), + depth: 0, } } @@ -128,6 +132,7 @@ func (iter *Iterator) Reset(reader io.Reader) *Iterator { iter.reader = reader iter.head = 0 iter.tail = 0 + iter.depth = 0 return iter } @@ -137,6 +142,7 @@ func (iter *Iterator) ResetBytes(input []byte) *Iterator { iter.buf = input iter.head = 0 iter.tail = len(input) + iter.depth = 0 return iter } @@ -320,3 +326,24 @@ func (iter *Iterator) Read() interface{} { return nil } } + +// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9 +const maxDepth = 10000 + +func (iter *Iterator) incrementDepth() (success bool) { + iter.depth++ + if iter.depth <= maxDepth { + return true + } + iter.ReportError("incrementDepth", "exceeded max depth") + return false +} + +func (iter *Iterator) decrementDepth() (success bool) { + iter.depth-- + if iter.depth >= 0 { + return true + } + iter.ReportError("decrementDepth", "unexpected negative nesting") + return false +} diff --git a/vendor/github.com/json-iterator/go/iter_array.go b/vendor/github.com/json-iterator/go/iter_array.go index 6188cb45..204fe0e0 100644 --- a/vendor/github.com/json-iterator/go/iter_array.go +++ b/vendor/github.com/json-iterator/go/iter_array.go @@ -28,26 +28,32 @@ func (iter *Iterator) ReadArray() (ret bool) { func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) { c := iter.nextToken() if c == '[' { + if !iter.incrementDepth() { + return false + } c = iter.nextToken() if c != ']' { iter.unreadByte() if !callback(iter) { + iter.decrementDepth() return false } c = iter.nextToken() for c == ',' { if !callback(iter) { + iter.decrementDepth() return false } c = iter.nextToken() } if c != ']' { iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c})) + iter.decrementDepth() return false } - return true + return iter.decrementDepth() } - return true + return iter.decrementDepth() } if c == 'n' { iter.skipThreeBytes('u', 'l', 'l') diff --git a/vendor/github.com/json-iterator/go/iter_float.go b/vendor/github.com/json-iterator/go/iter_float.go index b9754638..8a3d8b6f 100644 --- a/vendor/github.com/json-iterator/go/iter_float.go +++ b/vendor/github.com/json-iterator/go/iter_float.go @@ -288,6 +288,9 @@ non_decimal_loop: return iter.readFloat64SlowPath() } value = (value << 3) + (value << 1) + uint64(ind) + if value > maxFloat64 { + return iter.readFloat64SlowPath() + } } } return iter.readFloat64SlowPath() diff --git a/vendor/github.com/json-iterator/go/iter_int.go b/vendor/github.com/json-iterator/go/iter_int.go index 21423203..d786a89f 100644 --- a/vendor/github.com/json-iterator/go/iter_int.go +++ b/vendor/github.com/json-iterator/go/iter_int.go @@ -9,6 +9,7 @@ var intDigits []int8 const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1 const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1 +const maxFloat64 = 1<<53 - 1 func init() { intDigits = make([]int8, 256) @@ -339,7 +340,7 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) { } func (iter *Iterator) assertInteger() { - if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' { + if iter.head < iter.tail && iter.buf[iter.head] == '.' { iter.ReportError("assertInteger", "can not decode float as int") } } diff --git a/vendor/github.com/json-iterator/go/iter_object.go b/vendor/github.com/json-iterator/go/iter_object.go index 1c575767..58ee89c8 100644 --- a/vendor/github.com/json-iterator/go/iter_object.go +++ b/vendor/github.com/json-iterator/go/iter_object.go @@ -112,6 +112,9 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { c := iter.nextToken() var field string if c == '{' { + if !iter.incrementDepth() { + return false + } c = iter.nextToken() if c == '"' { iter.unreadByte() @@ -121,6 +124,7 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) } if !callback(iter, field) { + iter.decrementDepth() return false } c = iter.nextToken() @@ -131,20 +135,23 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) } if !callback(iter, field) { + iter.decrementDepth() return false } c = iter.nextToken() } if c != '}' { iter.ReportError("ReadObjectCB", `object not ended with }`) + iter.decrementDepth() return false } - return true + return iter.decrementDepth() } if c == '}' { - return true + return iter.decrementDepth() } - iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c})) + iter.decrementDepth() return false } if c == 'n' { @@ -159,15 +166,20 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { c := iter.nextToken() if c == '{' { + if !iter.incrementDepth() { + return false + } c = iter.nextToken() if c == '"' { iter.unreadByte() field := iter.ReadString() if iter.nextToken() != ':' { iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + iter.decrementDepth() return false } if !callback(iter, field) { + iter.decrementDepth() return false } c = iter.nextToken() @@ -175,23 +187,27 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { field = iter.ReadString() if iter.nextToken() != ':' { iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + iter.decrementDepth() return false } if !callback(iter, field) { + iter.decrementDepth() return false } c = iter.nextToken() } if c != '}' { iter.ReportError("ReadMapCB", `object not ended with }`) + iter.decrementDepth() return false } - return true + return iter.decrementDepth() } if c == '}' { - return true + return iter.decrementDepth() } - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c})) + iter.decrementDepth() return false } if c == 'n' { diff --git a/vendor/github.com/json-iterator/go/iter_skip.go b/vendor/github.com/json-iterator/go/iter_skip.go index f58beb91..e91eefb1 100644 --- a/vendor/github.com/json-iterator/go/iter_skip.go +++ b/vendor/github.com/json-iterator/go/iter_skip.go @@ -37,17 +37,24 @@ func (iter *Iterator) SkipAndReturnBytes() []byte { return iter.stopCapture() } -type captureBuffer struct { - startedAt int - captured []byte +// SkipAndAppendBytes skips next JSON element and appends its content to +// buffer, returning the result. +func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte { + iter.startCaptureTo(buf, iter.head) + iter.Skip() + return iter.stopCapture() } -func (iter *Iterator) startCapture(captureStartedAt int) { +func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) { if iter.captured != nil { panic("already in capture mode") } iter.captureStartedAt = captureStartedAt - iter.captured = make([]byte, 0, 32) + iter.captured = buf +} + +func (iter *Iterator) startCapture(captureStartedAt int) { + iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt) } func (iter *Iterator) stopCapture() []byte { @@ -58,13 +65,7 @@ func (iter *Iterator) stopCapture() []byte { remaining := iter.buf[iter.captureStartedAt:iter.head] iter.captureStartedAt = -1 iter.captured = nil - if len(captured) == 0 { - copied := make([]byte, len(remaining)) - copy(copied, remaining) - return copied - } - captured = append(captured, remaining...) - return captured + return append(captured, remaining...) } // Skip skips a json object and positions to relatively the next json object diff --git a/vendor/github.com/json-iterator/go/iter_skip_sloppy.go b/vendor/github.com/json-iterator/go/iter_skip_sloppy.go index 8fcdc3b6..9303de41 100644 --- a/vendor/github.com/json-iterator/go/iter_skip_sloppy.go +++ b/vendor/github.com/json-iterator/go/iter_skip_sloppy.go @@ -22,6 +22,9 @@ func (iter *Iterator) skipNumber() { func (iter *Iterator) skipArray() { level := 1 + if !iter.incrementDepth() { + return + } for { for i := iter.head; i < iter.tail; i++ { switch iter.buf[i] { @@ -31,8 +34,14 @@ func (iter *Iterator) skipArray() { i = iter.head - 1 // it will be i++ soon case '[': // If open symbol, increase level level++ + if !iter.incrementDepth() { + return + } case ']': // If close symbol, increase level level-- + if !iter.decrementDepth() { + return + } // If we have returned to the original level, we're done if level == 0 { @@ -50,6 +59,10 @@ func (iter *Iterator) skipArray() { func (iter *Iterator) skipObject() { level := 1 + if !iter.incrementDepth() { + return + } + for { for i := iter.head; i < iter.tail; i++ { switch iter.buf[i] { @@ -59,8 +72,14 @@ func (iter *Iterator) skipObject() { i = iter.head - 1 // it will be i++ soon case '{': // If open symbol, increase level level++ + if !iter.incrementDepth() { + return + } case '}': // If close symbol, increase level level-- + if !iter.decrementDepth() { + return + } // If we have returned to the original level, we're done if level == 0 { diff --git a/vendor/github.com/json-iterator/go/reflect.go b/vendor/github.com/json-iterator/go/reflect.go index 4459e203..39acb320 100644 --- a/vendor/github.com/json-iterator/go/reflect.go +++ b/vendor/github.com/json-iterator/go/reflect.go @@ -60,11 +60,12 @@ func (b *ctx) append(prefix string) *ctx { // ReadVal copy the underlying JSON into go interface, same as json.Unmarshal func (iter *Iterator) ReadVal(obj interface{}) { + depth := iter.depth cacheKey := reflect2.RTypeOf(obj) decoder := iter.cfg.getDecoderFromCache(cacheKey) if decoder == nil { typ := reflect2.TypeOf(obj) - if typ.Kind() != reflect.Ptr { + if typ == nil || typ.Kind() != reflect.Ptr { iter.ReportError("ReadVal", "can only unmarshal into pointer") return } @@ -76,6 +77,10 @@ func (iter *Iterator) ReadVal(obj interface{}) { return } decoder.Decode(ptr, iter) + if iter.depth != depth { + iter.ReportError("ReadVal", "unexpected mismatched nesting") + return + } } // WriteVal copy the go interface into underlying JSON, same as json.Marshal diff --git a/vendor/github.com/json-iterator/go/reflect_extension.go b/vendor/github.com/json-iterator/go/reflect_extension.go index 05e8fbf1..74a97bfe 100644 --- a/vendor/github.com/json-iterator/go/reflect_extension.go +++ b/vendor/github.com/json-iterator/go/reflect_extension.go @@ -341,10 +341,10 @@ func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor { if ctx.onlyTaggedField && !hastag && !field.Anonymous() { continue } - tagParts := strings.Split(tag, ",") - if tag == "-" { + if tag == "-" || field.Name() == "_" { continue } + tagParts := strings.Split(tag, ",") if field.Anonymous() && (tag == "" || tagParts[0] == "") { if field.Type().Kind() == reflect.Struct { structDescriptor := describeStruct(ctx, field.Type()) @@ -475,7 +475,7 @@ func calcFieldNames(originalFieldName string, tagProvidedFieldName string, whole fieldNames = []string{tagProvidedFieldName} } // private? - isNotExported := unicode.IsLower(rune(originalFieldName[0])) + isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_' if isNotExported { fieldNames = []string{} } diff --git a/vendor/github.com/json-iterator/go/reflect_json_raw_message.go b/vendor/github.com/json-iterator/go/reflect_json_raw_message.go index f2619936..eba434f2 100644 --- a/vendor/github.com/json-iterator/go/reflect_json_raw_message.go +++ b/vendor/github.com/json-iterator/go/reflect_json_raw_message.go @@ -33,11 +33,19 @@ type jsonRawMessageCodec struct { } func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - *((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes()) + if iter.ReadNil() { + *((*json.RawMessage)(ptr)) = nil + } else { + *((*json.RawMessage)(ptr)) = iter.SkipAndReturnBytes() + } } func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteRaw(string(*((*json.RawMessage)(ptr)))) + if *((*json.RawMessage)(ptr)) == nil { + stream.WriteNil() + } else { + stream.WriteRaw(string(*((*json.RawMessage)(ptr)))) + } } func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { @@ -48,11 +56,19 @@ type jsoniterRawMessageCodec struct { } func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - *((*RawMessage)(ptr)) = RawMessage(iter.SkipAndReturnBytes()) + if iter.ReadNil() { + *((*RawMessage)(ptr)) = nil + } else { + *((*RawMessage)(ptr)) = iter.SkipAndReturnBytes() + } } func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteRaw(string(*((*RawMessage)(ptr)))) + if *((*RawMessage)(ptr)) == nil { + stream.WriteNil() + } else { + stream.WriteRaw(string(*((*RawMessage)(ptr)))) + } } func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { diff --git a/vendor/github.com/json-iterator/go/reflect_map.go b/vendor/github.com/json-iterator/go/reflect_map.go index 547b4421..58296713 100644 --- a/vendor/github.com/json-iterator/go/reflect_map.go +++ b/vendor/github.com/json-iterator/go/reflect_map.go @@ -49,6 +49,33 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { return decoder } } + + ptrType := reflect2.PtrTo(typ) + if ptrType.Implements(unmarshalerType) { + return &referenceDecoder{ + &unmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(unmarshalerType) { + return &unmarshalerDecoder{ + valType: typ, + } + } + if ptrType.Implements(textUnmarshalerType) { + return &referenceDecoder{ + &textUnmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(textUnmarshalerType) { + return &textUnmarshalerDecoder{ + valType: typ, + } + } + switch typ.Kind() { case reflect.String: return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -63,31 +90,6 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyDecoder{decoderOfType(ctx, typ)} default: - ptrType := reflect2.PtrTo(typ) - if ptrType.Implements(unmarshalerType) { - return &referenceDecoder{ - &unmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(unmarshalerType) { - return &unmarshalerDecoder{ - valType: typ, - } - } - if ptrType.Implements(textUnmarshalerType) { - return &referenceDecoder{ - &textUnmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(textUnmarshalerType) { - return &textUnmarshalerDecoder{ - valType: typ, - } - } return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} } } @@ -103,6 +105,19 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { return encoder } } + + if typ == textMarshalerType { + return &directTextMarshalerEncoder{ + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + if typ.Implements(textMarshalerType) { + return &textMarshalerEncoder{ + valType: typ, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + switch typ.Kind() { case reflect.String: return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -117,17 +132,6 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyEncoder{encoderOfType(ctx, typ)} default: - if typ == textMarshalerType { - return &directTextMarshalerEncoder{ - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } - if typ.Implements(textMarshalerType) { - return &textMarshalerEncoder{ - valType: typ, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } if typ.Kind() == reflect.Interface { return &dynamicMapKeyEncoder{ctx, typ} } @@ -163,10 +167,6 @@ func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { if c == '}' { return } - if c != '"' { - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) - return - } iter.unreadByte() key := decoder.keyType.UnsafeNew() decoder.keyDecoder.Decode(key, iter) @@ -249,6 +249,10 @@ type mapEncoder struct { } func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if *(*unsafe.Pointer)(ptr) == nil { + stream.WriteNil() + return + } stream.WriteObjectStart() iter := encoder.mapType.UnsafeIterate(ptr) for i := 0; iter.HasNext(); i++ { @@ -286,16 +290,17 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { stream.WriteObjectStart() mapIter := encoder.mapType.UnsafeIterate(ptr) subStream := stream.cfg.BorrowStream(nil) + subStream.Attachment = stream.Attachment subIter := stream.cfg.BorrowIterator(nil) keyValues := encodedKeyValues{} for mapIter.HasNext() { - subStream.buf = make([]byte, 0, 64) key, elem := mapIter.UnsafeNext() + subStreamIndex := subStream.Buffered() encoder.keyEncoder.Encode(key, subStream) if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil { stream.Error = subStream.Error } - encodedKey := subStream.Buffer() + encodedKey := subStream.Buffer()[subStreamIndex:] subIter.ResetBytes(encodedKey) decodedKey := subIter.ReadString() if stream.indention > 0 { @@ -306,7 +311,7 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { encoder.elemEncoder.Encode(elem, subStream) keyValues = append(keyValues, encodedKV{ key: decodedKey, - keyValue: subStream.Buffer(), + keyValue: subStream.Buffer()[subStreamIndex:], }) } sort.Sort(keyValues) @@ -316,6 +321,9 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { } stream.Write(keyValue.keyValue) } + if subStream.Error != nil && stream.Error == nil { + stream.Error = subStream.Error + } stream.WriteObjectEnd() stream.cfg.ReturnStream(subStream) stream.cfg.ReturnIterator(subIter) diff --git a/vendor/github.com/json-iterator/go/reflect_marshaler.go b/vendor/github.com/json-iterator/go/reflect_marshaler.go index fea50719..3e21f375 100644 --- a/vendor/github.com/json-iterator/go/reflect_marshaler.go +++ b/vendor/github.com/json-iterator/go/reflect_marshaler.go @@ -3,8 +3,9 @@ package jsoniter import ( "encoding" "encoding/json" - "github.com/modern-go/reflect2" "unsafe" + + "github.com/modern-go/reflect2" ) var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem() @@ -93,10 +94,17 @@ func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { stream.WriteNil() return } - bytes, err := json.Marshal(obj) + marshaler := obj.(json.Marshaler) + bytes, err := marshaler.MarshalJSON() if err != nil { stream.Error = err } else { + // html escape was already done by jsoniter + // but the extra '\n' should be trimed + l := len(bytes) + if l > 0 && bytes[l-1] == '\n' { + bytes = bytes[:l-1] + } stream.Write(bytes) } } diff --git a/vendor/github.com/json-iterator/go/reflect_native.go b/vendor/github.com/json-iterator/go/reflect_native.go index 9042eb0c..f88722d1 100644 --- a/vendor/github.com/json-iterator/go/reflect_native.go +++ b/vendor/github.com/json-iterator/go/reflect_native.go @@ -432,17 +432,19 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { } func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - src := *((*[]byte)(ptr)) - if len(src) == 0 { + if codec.sliceType.UnsafeIsNil(ptr) { stream.WriteNil() return } + src := *((*[]byte)(ptr)) encoding := base64.StdEncoding stream.writeByte('"') - size := encoding.EncodedLen(len(src)) - buf := make([]byte, size) - encoding.Encode(buf, src) - stream.buf = append(stream.buf, buf...) + if len(src) != 0 { + size := encoding.EncodedLen(len(src)) + buf := make([]byte, size) + encoding.Encode(buf, src) + stream.buf = append(stream.buf, buf...) + } stream.writeByte('"') } diff --git a/vendor/github.com/json-iterator/go/reflect_optional.go b/vendor/github.com/json-iterator/go/reflect_optional.go index 43ec71d6..fa71f474 100644 --- a/vendor/github.com/json-iterator/go/reflect_optional.go +++ b/vendor/github.com/json-iterator/go/reflect_optional.go @@ -2,7 +2,6 @@ package jsoniter import ( "github.com/modern-go/reflect2" - "reflect" "unsafe" ) @@ -10,9 +9,6 @@ func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { ptrType := typ.(*reflect2.UnsafePtrType) elemType := ptrType.Elem() decoder := decoderOfType(ctx, elemType) - if ctx.prefix == "" && elemType.Kind() == reflect.Ptr { - return &dereferenceDecoder{elemType, decoder} - } return &OptionalDecoder{elemType, decoder} } diff --git a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go index 355d2d11..92ae912d 100644 --- a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go +++ b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go @@ -500,16 +500,20 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } var c byte for c = ','; c == ','; c = iter.nextToken() { decoder.decodeOneField(ptr, iter) } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } if c != '}' { iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c})) } + iter.decrementDepth() } func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) { @@ -530,8 +534,8 @@ func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *It } } if fieldDecoder == nil { - msg := "found unknown field: " + field if decoder.disallowUnknownFields { + msg := "found unknown field: " + field iter.ReportError("ReadObject", msg) } c := iter.nextToken() @@ -571,6 +575,9 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { if iter.readFieldHash() == decoder.fieldHash { decoder.fieldDecoder.Decode(ptr, iter) @@ -581,9 +588,10 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type twoFieldsStructDecoder struct { @@ -598,6 +606,9 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -611,9 +622,10 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type threeFieldsStructDecoder struct { @@ -630,6 +642,9 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -645,9 +660,10 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type fourFieldsStructDecoder struct { @@ -666,6 +682,9 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -683,9 +702,10 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type fiveFieldsStructDecoder struct { @@ -706,6 +726,9 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -725,9 +748,10 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type sixFieldsStructDecoder struct { @@ -750,6 +774,9 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -771,9 +798,10 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type sevenFieldsStructDecoder struct { @@ -798,6 +826,9 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -821,9 +852,10 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type eightFieldsStructDecoder struct { @@ -850,6 +882,9 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -875,9 +910,10 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type nineFieldsStructDecoder struct { @@ -906,6 +942,9 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -933,9 +972,10 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type tenFieldsStructDecoder struct { @@ -966,6 +1006,9 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator if !iter.readObjectStart() { return } + if !iter.incrementDepth() { + return + } for { switch iter.readFieldHash() { case decoder.fieldHash1: @@ -995,9 +1038,10 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } + iter.decrementDepth() } type structFieldDecoder struct { @@ -1031,6 +1075,11 @@ type stringModeNumberDecoder struct { } func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.WhatIsNext() == NilValue { + decoder.elemDecoder.Decode(ptr, iter) + return + } + c := iter.nextToken() if c != '"' { iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c})) diff --git a/vendor/github.com/json-iterator/go/reflect_struct_encoder.go b/vendor/github.com/json-iterator/go/reflect_struct_encoder.go index d0759cf6..152e3ef5 100644 --- a/vendor/github.com/json-iterator/go/reflect_struct_encoder.go +++ b/vendor/github.com/json-iterator/go/reflect_struct_encoder.go @@ -200,6 +200,7 @@ type stringModeStringEncoder struct { func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { tempStream := encoder.cfg.BorrowStream(nil) + tempStream.Attachment = stream.Attachment defer encoder.cfg.ReturnStream(tempStream) encoder.elemEncoder.Encode(ptr, tempStream) stream.WriteString(string(tempStream.Buffer())) diff --git a/vendor/github.com/json-iterator/go/stream.go b/vendor/github.com/json-iterator/go/stream.go index 17662fde..23d8a3ad 100644 --- a/vendor/github.com/json-iterator/go/stream.go +++ b/vendor/github.com/json-iterator/go/stream.go @@ -103,14 +103,14 @@ func (stream *Stream) Flush() error { if stream.Error != nil { return stream.Error } - n, err := stream.out.Write(stream.buf) + _, err := stream.out.Write(stream.buf) if err != nil { if stream.Error == nil { stream.Error = err } return err } - stream.buf = stream.buf[n:] + stream.buf = stream.buf[:0] return nil } @@ -177,7 +177,6 @@ func (stream *Stream) WriteEmptyObject() { func (stream *Stream) WriteMore() { stream.writeByte(',') stream.writeIndention(0) - stream.Flush() } // WriteArrayStart write [ with possible indention diff --git a/vendor/github.com/json-iterator/go/stream_float.go b/vendor/github.com/json-iterator/go/stream_float.go index f318d2c5..826aa594 100644 --- a/vendor/github.com/json-iterator/go/stream_float.go +++ b/vendor/github.com/json-iterator/go/stream_float.go @@ -1,6 +1,7 @@ package jsoniter import ( + "fmt" "math" "strconv" ) @@ -13,6 +14,10 @@ func init() { // WriteFloat32 write float32 to stream func (stream *Stream) WriteFloat32(val float32) { + if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } abs := math.Abs(float64(val)) fmt := byte('f') // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. @@ -26,6 +31,10 @@ func (stream *Stream) WriteFloat32(val float32) { // WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster func (stream *Stream) WriteFloat32Lossy(val float32) { + if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } if val < 0 { stream.writeByte('-') val = -val @@ -54,6 +63,10 @@ func (stream *Stream) WriteFloat32Lossy(val float32) { // WriteFloat64 write float64 to stream func (stream *Stream) WriteFloat64(val float64) { + if math.IsInf(val, 0) || math.IsNaN(val) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } abs := math.Abs(val) fmt := byte('f') // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. @@ -67,6 +80,10 @@ func (stream *Stream) WriteFloat64(val float64) { // WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster func (stream *Stream) WriteFloat64Lossy(val float64) { + if math.IsInf(val, 0) || math.IsNaN(val) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } if val < 0 { stream.writeByte('-') val = -val diff --git a/vendor/github.com/modern-go/reflect2/.travis.yml b/vendor/github.com/modern-go/reflect2/.travis.yml index fbb43744..b097728d 100644 --- a/vendor/github.com/modern-go/reflect2/.travis.yml +++ b/vendor/github.com/modern-go/reflect2/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.8.x + - 1.9.x - 1.x before_install: diff --git a/vendor/github.com/modern-go/reflect2/Gopkg.lock b/vendor/github.com/modern-go/reflect2/Gopkg.lock index 2a3a6989..10ef8111 100644 --- a/vendor/github.com/modern-go/reflect2/Gopkg.lock +++ b/vendor/github.com/modern-go/reflect2/Gopkg.lock @@ -1,15 +1,9 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. -[[projects]] - name = "github.com/modern-go/concurrent" - packages = ["."] - revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a" - version = "1.0.0" - [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "daee8a88b3498b61c5640056665b8b9eea062006f5e596bbb6a3ed9119a11ec7" + input-imports = [] solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/modern-go/reflect2/Gopkg.toml b/vendor/github.com/modern-go/reflect2/Gopkg.toml index 2f4f4dbd..a9bc5061 100644 --- a/vendor/github.com/modern-go/reflect2/Gopkg.toml +++ b/vendor/github.com/modern-go/reflect2/Gopkg.toml @@ -26,10 +26,6 @@ ignored = [] -[[constraint]] - name = "github.com/modern-go/concurrent" - version = "1.0.0" - [prune] go-tests = true unused-packages = true diff --git a/vendor/github.com/modern-go/reflect2/go.mod b/vendor/github.com/modern-go/reflect2/go.mod new file mode 100644 index 00000000..9057e9b3 --- /dev/null +++ b/vendor/github.com/modern-go/reflect2/go.mod @@ -0,0 +1,3 @@ +module github.com/modern-go/reflect2 + +go 1.12 diff --git a/vendor/github.com/modern-go/reflect2/go_above_118.go b/vendor/github.com/modern-go/reflect2/go_above_118.go new file mode 100644 index 00000000..2b4116f6 --- /dev/null +++ b/vendor/github.com/modern-go/reflect2/go_above_118.go @@ -0,0 +1,23 @@ +//+build go1.18 + +package reflect2 + +import ( + "unsafe" +) + +// m escapes into the return value, but the caller of mapiterinit +// doesn't let the return value escape. +//go:noescape +//go:linkname mapiterinit reflect.mapiterinit +func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer, it *hiter) + +func (type2 *UnsafeMapType) UnsafeIterate(obj unsafe.Pointer) MapIterator { + var it hiter + mapiterinit(type2.rtype, *(*unsafe.Pointer)(obj), &it) + return &UnsafeMapIterator{ + hiter: &it, + pKeyRType: type2.pKeyRType, + pElemRType: type2.pElemRType, + } +} \ No newline at end of file diff --git a/vendor/github.com/modern-go/reflect2/go_above_17.go b/vendor/github.com/modern-go/reflect2/go_above_17.go deleted file mode 100644 index 5c1cea86..00000000 --- a/vendor/github.com/modern-go/reflect2/go_above_17.go +++ /dev/null @@ -1,8 +0,0 @@ -//+build go1.7 - -package reflect2 - -import "unsafe" - -//go:linkname resolveTypeOff reflect.resolveTypeOff -func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer diff --git a/vendor/github.com/modern-go/reflect2/go_above_19.go b/vendor/github.com/modern-go/reflect2/go_above_19.go index c7e3b780..974f7685 100644 --- a/vendor/github.com/modern-go/reflect2/go_above_19.go +++ b/vendor/github.com/modern-go/reflect2/go_above_19.go @@ -6,6 +6,9 @@ import ( "unsafe" ) +//go:linkname resolveTypeOff reflect.resolveTypeOff +func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + //go:linkname makemap reflect.makemap func makemap(rtype unsafe.Pointer, cap int) (m unsafe.Pointer) diff --git a/vendor/github.com/modern-go/reflect2/go_below_118.go b/vendor/github.com/modern-go/reflect2/go_below_118.go new file mode 100644 index 00000000..00003dbd --- /dev/null +++ b/vendor/github.com/modern-go/reflect2/go_below_118.go @@ -0,0 +1,21 @@ +//+build !go1.18 + +package reflect2 + +import ( + "unsafe" +) + +// m escapes into the return value, but the caller of mapiterinit +// doesn't let the return value escape. +//go:noescape +//go:linkname mapiterinit reflect.mapiterinit +func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer) (val *hiter) + +func (type2 *UnsafeMapType) UnsafeIterate(obj unsafe.Pointer) MapIterator { + return &UnsafeMapIterator{ + hiter: mapiterinit(type2.rtype, *(*unsafe.Pointer)(obj)), + pKeyRType: type2.pKeyRType, + pElemRType: type2.pElemRType, + } +} \ No newline at end of file diff --git a/vendor/github.com/modern-go/reflect2/go_below_17.go b/vendor/github.com/modern-go/reflect2/go_below_17.go deleted file mode 100644 index 65a93c88..00000000 --- a/vendor/github.com/modern-go/reflect2/go_below_17.go +++ /dev/null @@ -1,9 +0,0 @@ -//+build !go1.7 - -package reflect2 - -import "unsafe" - -func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { - return nil -} diff --git a/vendor/github.com/modern-go/reflect2/go_below_19.go b/vendor/github.com/modern-go/reflect2/go_below_19.go deleted file mode 100644 index b050ef70..00000000 --- a/vendor/github.com/modern-go/reflect2/go_below_19.go +++ /dev/null @@ -1,14 +0,0 @@ -//+build !go1.9 - -package reflect2 - -import ( - "unsafe" -) - -//go:linkname makemap reflect.makemap -func makemap(rtype unsafe.Pointer) (m unsafe.Pointer) - -func makeMapWithSize(rtype unsafe.Pointer, cap int) unsafe.Pointer { - return makemap(rtype) -} diff --git a/vendor/github.com/modern-go/reflect2/reflect2.go b/vendor/github.com/modern-go/reflect2/reflect2.go index 63b49c79..c43c8b9d 100644 --- a/vendor/github.com/modern-go/reflect2/reflect2.go +++ b/vendor/github.com/modern-go/reflect2/reflect2.go @@ -1,8 +1,9 @@ package reflect2 import ( - "github.com/modern-go/concurrent" "reflect" + "runtime" + "sync" "unsafe" ) @@ -130,13 +131,13 @@ var ConfigSafe = Config{UseSafeImplementation: true}.Froze() type frozenConfig struct { useSafeImplementation bool - cache *concurrent.Map + cache *sync.Map } func (cfg Config) Froze() *frozenConfig { return &frozenConfig{ useSafeImplementation: cfg.UseSafeImplementation, - cache: concurrent.NewMap(), + cache: new(sync.Map), } } @@ -288,11 +289,12 @@ func NoEscape(p unsafe.Pointer) unsafe.Pointer { } func UnsafeCastString(str string) []byte { + bytes := make([]byte, 0) stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) - sliceHeader := &reflect.SliceHeader{ - Data: stringHeader.Data, - Cap: stringHeader.Len, - Len: stringHeader.Len, - } - return *(*[]byte)(unsafe.Pointer(sliceHeader)) + sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&bytes)) + sliceHeader.Data = stringHeader.Data + sliceHeader.Cap = stringHeader.Len + sliceHeader.Len = stringHeader.Len + runtime.KeepAlive(str) + return bytes } diff --git a/vendor/github.com/modern-go/reflect2/test.sh b/vendor/github.com/modern-go/reflect2/test.sh deleted file mode 100644 index 3d2b9768..00000000 --- a/vendor/github.com/modern-go/reflect2/test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list github.com/modern-go/reflect2-tests/... | grep -v vendor); do - go test -coverprofile=profile.out -coverpkg=github.com/modern-go/reflect2 $d - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/modern-go/reflect2/type_map.go b/vendor/github.com/modern-go/reflect2/type_map.go index 3acfb558..4b13c315 100644 --- a/vendor/github.com/modern-go/reflect2/type_map.go +++ b/vendor/github.com/modern-go/reflect2/type_map.go @@ -1,17 +1,13 @@ +// +build !gccgo + package reflect2 import ( "reflect" - "runtime" - "strings" "sync" "unsafe" ) -// typelinks1 for 1.5 ~ 1.6 -//go:linkname typelinks1 reflect.typelinks -func typelinks1() [][]unsafe.Pointer - // typelinks2 for 1.7 ~ //go:linkname typelinks2 reflect.typelinks func typelinks2() (sections []unsafe.Pointer, offset [][]int32) @@ -27,49 +23,10 @@ func discoverTypes() { types = make(map[string]reflect.Type) packages = make(map[string]map[string]reflect.Type) - ver := runtime.Version() - if ver == "go1.5" || strings.HasPrefix(ver, "go1.5.") { - loadGo15Types() - } else if ver == "go1.6" || strings.HasPrefix(ver, "go1.6.") { - loadGo15Types() - } else { - loadGo17Types() - } -} - -func loadGo15Types() { - var obj interface{} = reflect.TypeOf(0) - typePtrss := typelinks1() - for _, typePtrs := range typePtrss { - for _, typePtr := range typePtrs { - (*emptyInterface)(unsafe.Pointer(&obj)).word = typePtr - typ := obj.(reflect.Type) - if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct { - loadedType := typ.Elem() - pkgTypes := packages[loadedType.PkgPath()] - if pkgTypes == nil { - pkgTypes = map[string]reflect.Type{} - packages[loadedType.PkgPath()] = pkgTypes - } - types[loadedType.String()] = loadedType - pkgTypes[loadedType.Name()] = loadedType - } - if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Ptr && - typ.Elem().Elem().Kind() == reflect.Struct { - loadedType := typ.Elem().Elem() - pkgTypes := packages[loadedType.PkgPath()] - if pkgTypes == nil { - pkgTypes = map[string]reflect.Type{} - packages[loadedType.PkgPath()] = pkgTypes - } - types[loadedType.String()] = loadedType - pkgTypes[loadedType.Name()] = loadedType - } - } - } + loadGoTypes() } -func loadGo17Types() { +func loadGoTypes() { var obj interface{} = reflect.TypeOf(0) sections, offset := typelinks2() for i, offs := range offset { diff --git a/vendor/github.com/modern-go/reflect2/unsafe_link.go b/vendor/github.com/modern-go/reflect2/unsafe_link.go index 57229c8d..b49f614e 100644 --- a/vendor/github.com/modern-go/reflect2/unsafe_link.go +++ b/vendor/github.com/modern-go/reflect2/unsafe_link.go @@ -19,18 +19,12 @@ func typedslicecopy(elemType unsafe.Pointer, dst, src sliceHeader) int //go:linkname mapassign reflect.mapassign //go:noescape -func mapassign(rtype unsafe.Pointer, m unsafe.Pointer, key, val unsafe.Pointer) +func mapassign(rtype unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer, val unsafe.Pointer) //go:linkname mapaccess reflect.mapaccess //go:noescape func mapaccess(rtype unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) -// m escapes into the return value, but the caller of mapiterinit -// doesn't let the return value escape. -//go:noescape -//go:linkname mapiterinit reflect.mapiterinit -func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer) *hiter - //go:noescape //go:linkname mapiternext reflect.mapiternext func mapiternext(it *hiter) @@ -42,9 +36,21 @@ func ifaceE2I(rtype unsafe.Pointer, src interface{}, dst unsafe.Pointer) // If you modify hiter, also change cmd/internal/gc/reflect.go to indicate // the layout of this structure. type hiter struct { - key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/internal/gc/range.go). - value unsafe.Pointer // Must be in second position (see cmd/internal/gc/range.go). - // rest fields are ignored + key unsafe.Pointer + value unsafe.Pointer + t unsafe.Pointer + h unsafe.Pointer + buckets unsafe.Pointer + bptr unsafe.Pointer + overflow *[]unsafe.Pointer + oldoverflow *[]unsafe.Pointer + startBucket uintptr + offset uint8 + wrapped bool + B uint8 + i uint8 + bucket uintptr + checkBucket uintptr } // add returns p+x. diff --git a/vendor/github.com/modern-go/reflect2/unsafe_map.go b/vendor/github.com/modern-go/reflect2/unsafe_map.go index f2e76e6b..37872da8 100644 --- a/vendor/github.com/modern-go/reflect2/unsafe_map.go +++ b/vendor/github.com/modern-go/reflect2/unsafe_map.go @@ -107,14 +107,6 @@ func (type2 *UnsafeMapType) Iterate(obj interface{}) MapIterator { return type2.UnsafeIterate(objEFace.data) } -func (type2 *UnsafeMapType) UnsafeIterate(obj unsafe.Pointer) MapIterator { - return &UnsafeMapIterator{ - hiter: mapiterinit(type2.rtype, *(*unsafe.Pointer)(obj)), - pKeyRType: type2.pKeyRType, - pElemRType: type2.pElemRType, - } -} - type UnsafeMapIterator struct { *hiter pKeyRType unsafe.Pointer diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml deleted file mode 100644 index 055480b9..00000000 --- a/vendor/gopkg.in/yaml.v2/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go - -go: - - "1.4.x" - - "1.5.x" - - "1.6.x" - - "1.7.x" - - "1.8.x" - - "1.9.x" - - "1.10.x" - - "1.11.x" - - "1.12.x" - - "1.13.x" - - "tip" - -go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/go.mod b/vendor/gopkg.in/yaml.v2/go.mod deleted file mode 100644 index 1934e876..00000000 --- a/vendor/gopkg.in/yaml.v2/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module "gopkg.in/yaml.v2" - -require ( - "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 -) diff --git a/vendor/modules.txt b/vendor/modules.txt index 8043cee0..26035913 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,10 +1,16 @@ +# github.com/google/go-cmp v0.5.9 +github.com/google/go-cmp/cmp +github.com/google/go-cmp/cmp/internal/diff +github.com/google/go-cmp/cmp/internal/flags +github.com/google/go-cmp/cmp/internal/function +github.com/google/go-cmp/cmp/internal/value # github.com/google/gofuzz v1.0.0 github.com/google/gofuzz -# github.com/json-iterator/go v1.1.6 +# github.com/json-iterator/go v1.1.12 github.com/json-iterator/go # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd github.com/modern-go/concurrent -# github.com/modern-go/reflect2 v1.0.1 +# github.com/modern-go/reflect2 v1.0.2 github.com/modern-go/reflect2 -# gopkg.in/yaml.v2 v2.2.8 -gopkg.in/yaml.v2 +# sigs.k8s.io/yaml v1.4.0 +sigs.k8s.io/yaml/goyaml.v2 diff --git a/vendor/sigs.k8s.io/yaml/LICENSE b/vendor/sigs.k8s.io/yaml/LICENSE new file mode 100644 index 00000000..093d6d3e --- /dev/null +++ b/vendor/sigs.k8s.io/yaml/LICENSE @@ -0,0 +1,306 @@ +The MIT License (MIT) + +Copyright (c) 2014 Sam Ghods + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# The forked go-yaml.v3 library under this project is covered by two +different licenses (MIT and Apache): + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +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. + +# The forked go-yaml.v2 library under the project is covered by an +Apache license: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE similarity index 100% rename from vendor/gopkg.in/yaml.v2/LICENSE rename to vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE diff --git a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml b/vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE.libyaml similarity index 100% rename from vendor/gopkg.in/yaml.v2/LICENSE.libyaml rename to vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE.libyaml diff --git a/vendor/gopkg.in/yaml.v2/NOTICE b/vendor/sigs.k8s.io/yaml/goyaml.v2/NOTICE similarity index 100% rename from vendor/gopkg.in/yaml.v2/NOTICE rename to vendor/sigs.k8s.io/yaml/goyaml.v2/NOTICE diff --git a/vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS b/vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS new file mode 100644 index 00000000..73be0a3a --- /dev/null +++ b/vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS @@ -0,0 +1,24 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: +- dims +- jpbetz +- smarterclayton +- deads2k +- sttts +- liggitt +- natasha41575 +- knverey +reviewers: +- dims +- thockin +- jpbetz +- smarterclayton +- deads2k +- derekwaynecarr +- mikedanese +- liggitt +- sttts +- tallclair +labels: +- sig/api-machinery diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/sigs.k8s.io/yaml/goyaml.v2/README.md similarity index 84% rename from vendor/gopkg.in/yaml.v2/README.md rename to vendor/sigs.k8s.io/yaml/goyaml.v2/README.md index b50c6e87..53f4139d 100644 --- a/vendor/gopkg.in/yaml.v2/README.md +++ b/vendor/sigs.k8s.io/yaml/goyaml.v2/README.md @@ -1,3 +1,13 @@ +# go-yaml fork + +This package is a fork of the go-yaml library and is intended solely for consumption +by kubernetes projects. In this fork, we plan to support only critical changes required for +kubernetes, such as small bug fixes and regressions. Larger, general-purpose feature requests +should be made in the upstream go-yaml library, and we will reject such changes in this fork +unless we are pulling them from upstream. + +This fork is based on v2.4.0: https://github.com/go-yaml/yaml/releases/tag/v2.4.0 + # YAML support for the Go language Introduction diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/apic.go similarity index 99% rename from vendor/gopkg.in/yaml.v2/apic.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/apic.go index 1f7e87e6..acf71402 100644 --- a/vendor/gopkg.in/yaml.v2/apic.go +++ b/vendor/sigs.k8s.io/yaml/goyaml.v2/apic.go @@ -79,6 +79,8 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { parser.encoding = encoding } +var disableLineWrapping = false + // Create a new emitter object. func yaml_emitter_initialize(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{ @@ -87,6 +89,9 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) { states: make([]yaml_emitter_state_t, 0, initial_stack_size), events: make([]yaml_event_t, 0, initial_queue_size), } + if disableLineWrapping { + emitter.best_width = -1 + } } // Destroy an emitter object. diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/decode.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/decode.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/decode.go diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/emitterc.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/emitterc.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/emitterc.go diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/encode.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/encode.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/encode.go diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/parserc.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/parserc.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/parserc.go diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/readerc.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/readerc.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/readerc.go diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/resolve.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/resolve.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/resolve.go diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/scannerc.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/scannerc.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/scannerc.go diff --git a/vendor/gopkg.in/yaml.v2/sorter.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/sorter.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/sorter.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/sorter.go diff --git a/vendor/gopkg.in/yaml.v2/writerc.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/writerc.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/writerc.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/writerc.go diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/yaml.go similarity index 95% rename from vendor/gopkg.in/yaml.v2/yaml.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/yaml.go index 89650e29..30813884 100644 --- a/vendor/gopkg.in/yaml.v2/yaml.go +++ b/vendor/sigs.k8s.io/yaml/goyaml.v2/yaml.go @@ -175,7 +175,7 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) { // Zero valued structs will be omitted if all their public // fields are zero, unless they implement an IsZero // method (see the IsZeroer interface type), in which -// case the field will be included if that method returns true. +// case the field will be excluded if IsZero returns true. // // flow Marshal using a flow style (useful for structs, // sequences and maps). @@ -464,3 +464,15 @@ func isZero(v reflect.Value) bool { } return false } + +// FutureLineWrap globally disables line wrapping when encoding long strings. +// This is a temporary and thus deprecated method introduced to faciliate +// migration towards v3, which offers more control of line lengths on +// individual encodings, and has a default matching the behavior introduced +// by this function. +// +// The default formatting of v2 was erroneously changed in v2.3.0 and reverted +// in v2.4.0, at which point this function was introduced to help migration. +func FutureLineWrap() { + disableLineWrapping = true +} diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/yamlh.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/yamlh.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/yamlh.go diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/sigs.k8s.io/yaml/goyaml.v2/yamlprivateh.go similarity index 100% rename from vendor/gopkg.in/yaml.v2/yamlprivateh.go rename to vendor/sigs.k8s.io/yaml/goyaml.v2/yamlprivateh.go From b1dac8046e73f65c57f5892c31a0c91e8b8f5287 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 18 Nov 2024 18:28:37 +0000 Subject: [PATCH 077/105] Remove vendor directory --- vendor/github.com/google/go-cmp/LICENSE | 27 - .../github.com/google/go-cmp/cmp/compare.go | 669 ---- .../google/go-cmp/cmp/export_panic.go | 16 - .../google/go-cmp/cmp/export_unsafe.go | 36 - .../go-cmp/cmp/internal/diff/debug_disable.go | 18 - .../go-cmp/cmp/internal/diff/debug_enable.go | 123 - .../google/go-cmp/cmp/internal/diff/diff.go | 402 --- .../google/go-cmp/cmp/internal/flags/flags.go | 9 - .../go-cmp/cmp/internal/function/func.go | 99 - .../google/go-cmp/cmp/internal/value/name.go | 164 - .../cmp/internal/value/pointer_purego.go | 34 - .../cmp/internal/value/pointer_unsafe.go | 37 - .../google/go-cmp/cmp/internal/value/sort.go | 106 - .../github.com/google/go-cmp/cmp/options.go | 554 ---- vendor/github.com/google/go-cmp/cmp/path.go | 380 --- vendor/github.com/google/go-cmp/cmp/report.go | 54 - .../google/go-cmp/cmp/report_compare.go | 433 --- .../google/go-cmp/cmp/report_references.go | 264 -- .../google/go-cmp/cmp/report_reflect.go | 414 --- .../google/go-cmp/cmp/report_slices.go | 614 ---- .../google/go-cmp/cmp/report_text.go | 432 --- .../google/go-cmp/cmp/report_value.go | 121 - vendor/github.com/google/gofuzz/.travis.yml | 13 - .../github.com/google/gofuzz/CONTRIBUTING.md | 67 - vendor/github.com/google/gofuzz/LICENSE | 202 -- vendor/github.com/google/gofuzz/README.md | 71 - vendor/github.com/google/gofuzz/doc.go | 18 - vendor/github.com/google/gofuzz/fuzz.go | 487 --- vendor/github.com/google/gofuzz/go.mod | 3 - .../github.com/json-iterator/go/.codecov.yml | 3 - vendor/github.com/json-iterator/go/.gitignore | 4 - .../github.com/json-iterator/go/.travis.yml | 14 - vendor/github.com/json-iterator/go/Gopkg.lock | 21 - vendor/github.com/json-iterator/go/Gopkg.toml | 26 - vendor/github.com/json-iterator/go/LICENSE | 21 - vendor/github.com/json-iterator/go/README.md | 85 - vendor/github.com/json-iterator/go/adapter.go | 150 - vendor/github.com/json-iterator/go/any.go | 325 -- .../github.com/json-iterator/go/any_array.go | 278 -- .../github.com/json-iterator/go/any_bool.go | 137 - .../github.com/json-iterator/go/any_float.go | 83 - .../github.com/json-iterator/go/any_int32.go | 74 - .../github.com/json-iterator/go/any_int64.go | 74 - .../json-iterator/go/any_invalid.go | 82 - vendor/github.com/json-iterator/go/any_nil.go | 69 - .../github.com/json-iterator/go/any_number.go | 123 - .../github.com/json-iterator/go/any_object.go | 374 --- vendor/github.com/json-iterator/go/any_str.go | 166 - .../github.com/json-iterator/go/any_uint32.go | 74 - .../github.com/json-iterator/go/any_uint64.go | 74 - vendor/github.com/json-iterator/go/build.sh | 12 - vendor/github.com/json-iterator/go/config.go | 375 --- .../go/fuzzy_mode_convert_table.md | 7 - vendor/github.com/json-iterator/go/go.mod | 11 - vendor/github.com/json-iterator/go/go.sum | 14 - vendor/github.com/json-iterator/go/iter.go | 349 --- .../github.com/json-iterator/go/iter_array.go | 64 - .../github.com/json-iterator/go/iter_float.go | 342 --- .../github.com/json-iterator/go/iter_int.go | 346 --- .../json-iterator/go/iter_object.go | 267 -- .../github.com/json-iterator/go/iter_skip.go | 130 - .../json-iterator/go/iter_skip_sloppy.go | 163 - .../json-iterator/go/iter_skip_strict.go | 99 - .../github.com/json-iterator/go/iter_str.go | 215 -- .../github.com/json-iterator/go/jsoniter.go | 18 - vendor/github.com/json-iterator/go/pool.go | 42 - vendor/github.com/json-iterator/go/reflect.go | 337 -- .../json-iterator/go/reflect_array.go | 104 - .../json-iterator/go/reflect_dynamic.go | 70 - .../json-iterator/go/reflect_extension.go | 483 --- .../json-iterator/go/reflect_json_number.go | 112 - .../go/reflect_json_raw_message.go | 76 - .../json-iterator/go/reflect_map.go | 346 --- .../json-iterator/go/reflect_marshaler.go | 225 -- .../json-iterator/go/reflect_native.go | 453 --- .../json-iterator/go/reflect_optional.go | 129 - .../json-iterator/go/reflect_slice.go | 99 - .../go/reflect_struct_decoder.go | 1097 ------- .../go/reflect_struct_encoder.go | 211 -- vendor/github.com/json-iterator/go/stream.go | 210 -- .../json-iterator/go/stream_float.go | 111 - .../github.com/json-iterator/go/stream_int.go | 190 -- .../github.com/json-iterator/go/stream_str.go | 372 --- vendor/github.com/json-iterator/go/test.sh | 12 - .../modern-go/concurrent/.gitignore | 1 - .../modern-go/concurrent/.travis.yml | 14 - .../github.com/modern-go/concurrent/LICENSE | 201 -- .../github.com/modern-go/concurrent/README.md | 49 - .../modern-go/concurrent/executor.go | 14 - .../modern-go/concurrent/go_above_19.go | 15 - .../modern-go/concurrent/go_below_19.go | 33 - vendor/github.com/modern-go/concurrent/log.go | 13 - .../github.com/modern-go/concurrent/test.sh | 12 - .../concurrent/unbounded_executor.go | 119 - .../github.com/modern-go/reflect2/.gitignore | 2 - .../github.com/modern-go/reflect2/.travis.yml | 15 - .../github.com/modern-go/reflect2/Gopkg.lock | 9 - .../github.com/modern-go/reflect2/Gopkg.toml | 31 - vendor/github.com/modern-go/reflect2/LICENSE | 201 -- .../github.com/modern-go/reflect2/README.md | 71 - vendor/github.com/modern-go/reflect2/go.mod | 3 - .../modern-go/reflect2/go_above_118.go | 23 - .../modern-go/reflect2/go_above_19.go | 17 - .../modern-go/reflect2/go_below_118.go | 21 - .../github.com/modern-go/reflect2/reflect2.go | 300 -- .../modern-go/reflect2/reflect2_amd64.s | 0 .../modern-go/reflect2/reflect2_kind.go | 30 - .../modern-go/reflect2/relfect2_386.s | 0 .../modern-go/reflect2/relfect2_amd64p32.s | 0 .../modern-go/reflect2/relfect2_arm.s | 0 .../modern-go/reflect2/relfect2_arm64.s | 0 .../modern-go/reflect2/relfect2_mips64x.s | 0 .../modern-go/reflect2/relfect2_mipsx.s | 0 .../modern-go/reflect2/relfect2_ppc64x.s | 0 .../modern-go/reflect2/relfect2_s390x.s | 0 .../modern-go/reflect2/safe_field.go | 58 - .../github.com/modern-go/reflect2/safe_map.go | 101 - .../modern-go/reflect2/safe_slice.go | 92 - .../modern-go/reflect2/safe_struct.go | 29 - .../modern-go/reflect2/safe_type.go | 78 - .../github.com/modern-go/reflect2/type_map.go | 70 - .../modern-go/reflect2/unsafe_array.go | 65 - .../modern-go/reflect2/unsafe_eface.go | 59 - .../modern-go/reflect2/unsafe_field.go | 74 - .../modern-go/reflect2/unsafe_iface.go | 64 - .../modern-go/reflect2/unsafe_link.go | 76 - .../modern-go/reflect2/unsafe_map.go | 130 - .../modern-go/reflect2/unsafe_ptr.go | 46 - .../modern-go/reflect2/unsafe_slice.go | 177 -- .../modern-go/reflect2/unsafe_struct.go | 59 - .../modern-go/reflect2/unsafe_type.go | 85 - vendor/modules.txt | 16 - vendor/sigs.k8s.io/yaml/LICENSE | 306 -- vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE | 201 -- .../yaml/goyaml.v2/LICENSE.libyaml | 31 - vendor/sigs.k8s.io/yaml/goyaml.v2/NOTICE | 13 - vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS | 24 - vendor/sigs.k8s.io/yaml/goyaml.v2/README.md | 143 - vendor/sigs.k8s.io/yaml/goyaml.v2/apic.go | 744 ----- vendor/sigs.k8s.io/yaml/goyaml.v2/decode.go | 815 ----- vendor/sigs.k8s.io/yaml/goyaml.v2/emitterc.go | 1685 ---------- vendor/sigs.k8s.io/yaml/goyaml.v2/encode.go | 390 --- vendor/sigs.k8s.io/yaml/goyaml.v2/parserc.go | 1095 ------- vendor/sigs.k8s.io/yaml/goyaml.v2/readerc.go | 412 --- vendor/sigs.k8s.io/yaml/goyaml.v2/resolve.go | 258 -- vendor/sigs.k8s.io/yaml/goyaml.v2/scannerc.go | 2711 ----------------- vendor/sigs.k8s.io/yaml/goyaml.v2/sorter.go | 113 - vendor/sigs.k8s.io/yaml/goyaml.v2/writerc.go | 26 - vendor/sigs.k8s.io/yaml/goyaml.v2/yaml.go | 478 --- vendor/sigs.k8s.io/yaml/goyaml.v2/yamlh.go | 739 ----- .../yaml/goyaml.v2/yamlprivateh.go | 173 -- 151 files changed, 28045 deletions(-) delete mode 100644 vendor/github.com/google/go-cmp/LICENSE delete mode 100644 vendor/github.com/google/go-cmp/cmp/compare.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/export_panic.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/export_unsafe.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/function/func.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/name.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/sort.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/options.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/path.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_compare.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_references.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_reflect.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_slices.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_text.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/report_value.go delete mode 100644 vendor/github.com/google/gofuzz/.travis.yml delete mode 100644 vendor/github.com/google/gofuzz/CONTRIBUTING.md delete mode 100644 vendor/github.com/google/gofuzz/LICENSE delete mode 100644 vendor/github.com/google/gofuzz/README.md delete mode 100644 vendor/github.com/google/gofuzz/doc.go delete mode 100644 vendor/github.com/google/gofuzz/fuzz.go delete mode 100644 vendor/github.com/google/gofuzz/go.mod delete mode 100644 vendor/github.com/json-iterator/go/.codecov.yml delete mode 100644 vendor/github.com/json-iterator/go/.gitignore delete mode 100644 vendor/github.com/json-iterator/go/.travis.yml delete mode 100644 vendor/github.com/json-iterator/go/Gopkg.lock delete mode 100644 vendor/github.com/json-iterator/go/Gopkg.toml delete mode 100644 vendor/github.com/json-iterator/go/LICENSE delete mode 100644 vendor/github.com/json-iterator/go/README.md delete mode 100644 vendor/github.com/json-iterator/go/adapter.go delete mode 100644 vendor/github.com/json-iterator/go/any.go delete mode 100644 vendor/github.com/json-iterator/go/any_array.go delete mode 100644 vendor/github.com/json-iterator/go/any_bool.go delete mode 100644 vendor/github.com/json-iterator/go/any_float.go delete mode 100644 vendor/github.com/json-iterator/go/any_int32.go delete mode 100644 vendor/github.com/json-iterator/go/any_int64.go delete mode 100644 vendor/github.com/json-iterator/go/any_invalid.go delete mode 100644 vendor/github.com/json-iterator/go/any_nil.go delete mode 100644 vendor/github.com/json-iterator/go/any_number.go delete mode 100644 vendor/github.com/json-iterator/go/any_object.go delete mode 100644 vendor/github.com/json-iterator/go/any_str.go delete mode 100644 vendor/github.com/json-iterator/go/any_uint32.go delete mode 100644 vendor/github.com/json-iterator/go/any_uint64.go delete mode 100644 vendor/github.com/json-iterator/go/build.sh delete mode 100644 vendor/github.com/json-iterator/go/config.go delete mode 100644 vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md delete mode 100644 vendor/github.com/json-iterator/go/go.mod delete mode 100644 vendor/github.com/json-iterator/go/go.sum delete mode 100644 vendor/github.com/json-iterator/go/iter.go delete mode 100644 vendor/github.com/json-iterator/go/iter_array.go delete mode 100644 vendor/github.com/json-iterator/go/iter_float.go delete mode 100644 vendor/github.com/json-iterator/go/iter_int.go delete mode 100644 vendor/github.com/json-iterator/go/iter_object.go delete mode 100644 vendor/github.com/json-iterator/go/iter_skip.go delete mode 100644 vendor/github.com/json-iterator/go/iter_skip_sloppy.go delete mode 100644 vendor/github.com/json-iterator/go/iter_skip_strict.go delete mode 100644 vendor/github.com/json-iterator/go/iter_str.go delete mode 100644 vendor/github.com/json-iterator/go/jsoniter.go delete mode 100644 vendor/github.com/json-iterator/go/pool.go delete mode 100644 vendor/github.com/json-iterator/go/reflect.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_array.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_dynamic.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_extension.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_json_number.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_json_raw_message.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_map.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_marshaler.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_native.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_optional.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_slice.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_struct_decoder.go delete mode 100644 vendor/github.com/json-iterator/go/reflect_struct_encoder.go delete mode 100644 vendor/github.com/json-iterator/go/stream.go delete mode 100644 vendor/github.com/json-iterator/go/stream_float.go delete mode 100644 vendor/github.com/json-iterator/go/stream_int.go delete mode 100644 vendor/github.com/json-iterator/go/stream_str.go delete mode 100644 vendor/github.com/json-iterator/go/test.sh delete mode 100644 vendor/github.com/modern-go/concurrent/.gitignore delete mode 100644 vendor/github.com/modern-go/concurrent/.travis.yml delete mode 100644 vendor/github.com/modern-go/concurrent/LICENSE delete mode 100644 vendor/github.com/modern-go/concurrent/README.md delete mode 100644 vendor/github.com/modern-go/concurrent/executor.go delete mode 100644 vendor/github.com/modern-go/concurrent/go_above_19.go delete mode 100644 vendor/github.com/modern-go/concurrent/go_below_19.go delete mode 100644 vendor/github.com/modern-go/concurrent/log.go delete mode 100644 vendor/github.com/modern-go/concurrent/test.sh delete mode 100644 vendor/github.com/modern-go/concurrent/unbounded_executor.go delete mode 100644 vendor/github.com/modern-go/reflect2/.gitignore delete mode 100644 vendor/github.com/modern-go/reflect2/.travis.yml delete mode 100644 vendor/github.com/modern-go/reflect2/Gopkg.lock delete mode 100644 vendor/github.com/modern-go/reflect2/Gopkg.toml delete mode 100644 vendor/github.com/modern-go/reflect2/LICENSE delete mode 100644 vendor/github.com/modern-go/reflect2/README.md delete mode 100644 vendor/github.com/modern-go/reflect2/go.mod delete mode 100644 vendor/github.com/modern-go/reflect2/go_above_118.go delete mode 100644 vendor/github.com/modern-go/reflect2/go_above_19.go delete mode 100644 vendor/github.com/modern-go/reflect2/go_below_118.go delete mode 100644 vendor/github.com/modern-go/reflect2/reflect2.go delete mode 100644 vendor/github.com/modern-go/reflect2/reflect2_amd64.s delete mode 100644 vendor/github.com/modern-go/reflect2/reflect2_kind.go delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_386.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_amd64p32.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_arm.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_arm64.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_mips64x.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_mipsx.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_ppc64x.s delete mode 100644 vendor/github.com/modern-go/reflect2/relfect2_s390x.s delete mode 100644 vendor/github.com/modern-go/reflect2/safe_field.go delete mode 100644 vendor/github.com/modern-go/reflect2/safe_map.go delete mode 100644 vendor/github.com/modern-go/reflect2/safe_slice.go delete mode 100644 vendor/github.com/modern-go/reflect2/safe_struct.go delete mode 100644 vendor/github.com/modern-go/reflect2/safe_type.go delete mode 100644 vendor/github.com/modern-go/reflect2/type_map.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_array.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_eface.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_field.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_iface.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_link.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_map.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_ptr.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_slice.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_struct.go delete mode 100644 vendor/github.com/modern-go/reflect2/unsafe_type.go delete mode 100644 vendor/modules.txt delete mode 100644 vendor/sigs.k8s.io/yaml/LICENSE delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/LICENSE.libyaml delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/NOTICE delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/OWNERS delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/README.md delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/apic.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/decode.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/emitterc.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/encode.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/parserc.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/readerc.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/resolve.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/scannerc.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/sorter.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/writerc.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/yaml.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/yamlh.go delete mode 100644 vendor/sigs.k8s.io/yaml/goyaml.v2/yamlprivateh.go diff --git a/vendor/github.com/google/go-cmp/LICENSE b/vendor/github.com/google/go-cmp/LICENSE deleted file mode 100644 index 32017f8f..00000000 --- a/vendor/github.com/google/go-cmp/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2017 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go deleted file mode 100644 index 087320da..00000000 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ /dev/null @@ -1,669 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package cmp determines equality of values. -// -// This package is intended to be a more powerful and safer alternative to -// reflect.DeepEqual for comparing whether two values are semantically equal. -// It is intended to only be used in tests, as performance is not a goal and -// it may panic if it cannot compare the values. Its propensity towards -// panicking means that its unsuitable for production environments where a -// spurious panic may be fatal. -// -// The primary features of cmp are: -// -// - When the default behavior of equality does not suit the test's needs, -// custom equality functions can override the equality operation. -// For example, an equality function may report floats as equal so long as -// they are within some tolerance of each other. -// -// - Types with an Equal method may use that method to determine equality. -// This allows package authors to determine the equality operation -// for the types that they define. -// -// - If no custom equality functions are used and no Equal method is defined, -// equality is determined by recursively comparing the primitive kinds on -// both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, -// unexported fields are not compared by default; they result in panics -// unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported) -// or explicitly compared using the Exporter option. -package cmp - -import ( - "fmt" - "reflect" - "strings" - - "github.com/google/go-cmp/cmp/internal/diff" - "github.com/google/go-cmp/cmp/internal/function" - "github.com/google/go-cmp/cmp/internal/value" -) - -// TODO(≥go1.18): Use any instead of interface{}. - -// Equal reports whether x and y are equal by recursively applying the -// following rules in the given order to x and y and all of their sub-values: -// -// - Let S be the set of all Ignore, Transformer, and Comparer options that -// remain after applying all path filters, value filters, and type filters. -// If at least one Ignore exists in S, then the comparison is ignored. -// If the number of Transformer and Comparer options in S is non-zero, -// then Equal panics because it is ambiguous which option to use. -// If S contains a single Transformer, then use that to transform -// the current values and recursively call Equal on the output values. -// If S contains a single Comparer, then use that to compare the current values. -// Otherwise, evaluation proceeds to the next rule. -// -// - If the values have an Equal method of the form "(T) Equal(T) bool" or -// "(T) Equal(I) bool" where T is assignable to I, then use the result of -// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and -// evaluation proceeds to the next rule. -// -// - Lastly, try to compare x and y based on their basic kinds. -// Simple kinds like booleans, integers, floats, complex numbers, strings, -// and channels are compared using the equivalent of the == operator in Go. -// Functions are only equal if they are both nil, otherwise they are unequal. -// -// Structs are equal if recursively calling Equal on all fields report equal. -// If a struct contains unexported fields, Equal panics unless an Ignore option -// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option -// explicitly permits comparing the unexported field. -// -// Slices are equal if they are both nil or both non-nil, where recursively -// calling Equal on all non-ignored slice or array elements report equal. -// Empty non-nil slices and nil slices are not equal; to equate empty slices, -// consider using cmpopts.EquateEmpty. -// -// Maps are equal if they are both nil or both non-nil, where recursively -// calling Equal on all non-ignored map entries report equal. -// Map keys are equal according to the == operator. -// To use custom comparisons for map keys, consider using cmpopts.SortMaps. -// Empty non-nil maps and nil maps are not equal; to equate empty maps, -// consider using cmpopts.EquateEmpty. -// -// Pointers and interfaces are equal if they are both nil or both non-nil, -// where they have the same underlying concrete type and recursively -// calling Equal on the underlying values reports equal. -// -// Before recursing into a pointer, slice element, or map, the current path -// is checked to detect whether the address has already been visited. -// If there is a cycle, then the pointed at values are considered equal -// only if both addresses were previously visited in the same path step. -func Equal(x, y interface{}, opts ...Option) bool { - s := newState(opts) - s.compareAny(rootStep(x, y)) - return s.result.Equal() -} - -// Diff returns a human-readable report of the differences between two values: -// y - x. It returns an empty string if and only if Equal returns true for the -// same input values and options. -// -// The output is displayed as a literal in pseudo-Go syntax. -// At the start of each line, a "-" prefix indicates an element removed from x, -// a "+" prefix to indicates an element added from y, and the lack of a prefix -// indicates an element common to both x and y. If possible, the output -// uses fmt.Stringer.String or error.Error methods to produce more humanly -// readable outputs. In such cases, the string is prefixed with either an -// 's' or 'e' character, respectively, to indicate that the method was called. -// -// Do not depend on this output being stable. If you need the ability to -// programmatically interpret the difference, consider using a custom Reporter. -func Diff(x, y interface{}, opts ...Option) string { - s := newState(opts) - - // Optimization: If there are no other reporters, we can optimize for the - // common case where the result is equal (and thus no reported difference). - // This avoids the expensive construction of a difference tree. - if len(s.reporters) == 0 { - s.compareAny(rootStep(x, y)) - if s.result.Equal() { - return "" - } - s.result = diff.Result{} // Reset results - } - - r := new(defaultReporter) - s.reporters = append(s.reporters, reporter{r}) - s.compareAny(rootStep(x, y)) - d := r.String() - if (d == "") != s.result.Equal() { - panic("inconsistent difference and equality results") - } - return d -} - -// rootStep constructs the first path step. If x and y have differing types, -// then they are stored within an empty interface type. -func rootStep(x, y interface{}) PathStep { - vx := reflect.ValueOf(x) - vy := reflect.ValueOf(y) - - // If the inputs are different types, auto-wrap them in an empty interface - // so that they have the same parent type. - var t reflect.Type - if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { - t = anyType - if vx.IsValid() { - vvx := reflect.New(t).Elem() - vvx.Set(vx) - vx = vvx - } - if vy.IsValid() { - vvy := reflect.New(t).Elem() - vvy.Set(vy) - vy = vvy - } - } else { - t = vx.Type() - } - - return &pathStep{t, vx, vy} -} - -type state struct { - // These fields represent the "comparison state". - // Calling statelessCompare must not result in observable changes to these. - result diff.Result // The current result of comparison - curPath Path // The current path in the value tree - curPtrs pointerPath // The current set of visited pointers - reporters []reporter // Optional reporters - - // recChecker checks for infinite cycles applying the same set of - // transformers upon the output of itself. - recChecker recChecker - - // dynChecker triggers pseudo-random checks for option correctness. - // It is safe for statelessCompare to mutate this value. - dynChecker dynChecker - - // These fields, once set by processOption, will not change. - exporters []exporter // List of exporters for structs with unexported fields - opts Options // List of all fundamental and filter options -} - -func newState(opts []Option) *state { - // Always ensure a validator option exists to validate the inputs. - s := &state{opts: Options{validator{}}} - s.curPtrs.Init() - s.processOption(Options(opts)) - return s -} - -func (s *state) processOption(opt Option) { - switch opt := opt.(type) { - case nil: - case Options: - for _, o := range opt { - s.processOption(o) - } - case coreOption: - type filtered interface { - isFiltered() bool - } - if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { - panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) - } - s.opts = append(s.opts, opt) - case exporter: - s.exporters = append(s.exporters, opt) - case reporter: - s.reporters = append(s.reporters, opt) - default: - panic(fmt.Sprintf("unknown option %T", opt)) - } -} - -// statelessCompare compares two values and returns the result. -// This function is stateless in that it does not alter the current result, -// or output to any registered reporters. -func (s *state) statelessCompare(step PathStep) diff.Result { - // We do not save and restore curPath and curPtrs because all of the - // compareX methods should properly push and pop from them. - // It is an implementation bug if the contents of the paths differ from - // when calling this function to when returning from it. - - oldResult, oldReporters := s.result, s.reporters - s.result = diff.Result{} // Reset result - s.reporters = nil // Remove reporters to avoid spurious printouts - s.compareAny(step) - res := s.result - s.result, s.reporters = oldResult, oldReporters - return res -} - -func (s *state) compareAny(step PathStep) { - // Update the path stack. - s.curPath.push(step) - defer s.curPath.pop() - for _, r := range s.reporters { - r.PushStep(step) - defer r.PopStep() - } - s.recChecker.Check(s.curPath) - - // Cycle-detection for slice elements (see NOTE in compareSlice). - t := step.Type() - vx, vy := step.Values() - if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { - px, py := vx.Addr(), vy.Addr() - if eq, visited := s.curPtrs.Push(px, py); visited { - s.report(eq, reportByCycle) - return - } - defer s.curPtrs.Pop(px, py) - } - - // Rule 1: Check whether an option applies on this node in the value tree. - if s.tryOptions(t, vx, vy) { - return - } - - // Rule 2: Check whether the type has a valid Equal method. - if s.tryMethod(t, vx, vy) { - return - } - - // Rule 3: Compare based on the underlying kind. - switch t.Kind() { - case reflect.Bool: - s.report(vx.Bool() == vy.Bool(), 0) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - s.report(vx.Int() == vy.Int(), 0) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - s.report(vx.Uint() == vy.Uint(), 0) - case reflect.Float32, reflect.Float64: - s.report(vx.Float() == vy.Float(), 0) - case reflect.Complex64, reflect.Complex128: - s.report(vx.Complex() == vy.Complex(), 0) - case reflect.String: - s.report(vx.String() == vy.String(), 0) - case reflect.Chan, reflect.UnsafePointer: - s.report(vx.Pointer() == vy.Pointer(), 0) - case reflect.Func: - s.report(vx.IsNil() && vy.IsNil(), 0) - case reflect.Struct: - s.compareStruct(t, vx, vy) - case reflect.Slice, reflect.Array: - s.compareSlice(t, vx, vy) - case reflect.Map: - s.compareMap(t, vx, vy) - case reflect.Ptr: - s.comparePtr(t, vx, vy) - case reflect.Interface: - s.compareInterface(t, vx, vy) - default: - panic(fmt.Sprintf("%v kind not handled", t.Kind())) - } -} - -func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { - // Evaluate all filters and apply the remaining options. - if opt := s.opts.filter(s, t, vx, vy); opt != nil { - opt.apply(s, vx, vy) - return true - } - return false -} - -func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { - // Check if this type even has an Equal method. - m, ok := t.MethodByName("Equal") - if !ok || !function.IsType(m.Type, function.EqualAssignable) { - return false - } - - eq := s.callTTBFunc(m.Func, vx, vy) - s.report(eq, reportByMethod) - return true -} - -func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { - if !s.dynChecker.Next() { - return f.Call([]reflect.Value{v})[0] - } - - // Run the function twice and ensure that we get the same results back. - // We run in goroutines so that the race detector (if enabled) can detect - // unsafe mutations to the input. - c := make(chan reflect.Value) - go detectRaces(c, f, v) - got := <-c - want := f.Call([]reflect.Value{v})[0] - if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { - // To avoid false-positives with non-reflexive equality operations, - // we sanity check whether a value is equal to itself. - if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { - return want - } - panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) - } - return want -} - -func (s *state) callTTBFunc(f, x, y reflect.Value) bool { - if !s.dynChecker.Next() { - return f.Call([]reflect.Value{x, y})[0].Bool() - } - - // Swapping the input arguments is sufficient to check that - // f is symmetric and deterministic. - // We run in goroutines so that the race detector (if enabled) can detect - // unsafe mutations to the input. - c := make(chan reflect.Value) - go detectRaces(c, f, y, x) - got := <-c - want := f.Call([]reflect.Value{x, y})[0].Bool() - if !got.IsValid() || got.Bool() != want { - panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) - } - return want -} - -func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { - var ret reflect.Value - defer func() { - recover() // Ignore panics, let the other call to f panic instead - c <- ret - }() - ret = f.Call(vs)[0] -} - -func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { - var addr bool - var vax, vay reflect.Value // Addressable versions of vx and vy - - var mayForce, mayForceInit bool - step := StructField{&structField{}} - for i := 0; i < t.NumField(); i++ { - step.typ = t.Field(i).Type - step.vx = vx.Field(i) - step.vy = vy.Field(i) - step.name = t.Field(i).Name - step.idx = i - step.unexported = !isExported(step.name) - if step.unexported { - if step.name == "_" { - continue - } - // Defer checking of unexported fields until later to give an - // Ignore a chance to ignore the field. - if !vax.IsValid() || !vay.IsValid() { - // For retrieveUnexportedField to work, the parent struct must - // be addressable. Create a new copy of the values if - // necessary to make them addressable. - addr = vx.CanAddr() || vy.CanAddr() - vax = makeAddressable(vx) - vay = makeAddressable(vy) - } - if !mayForceInit { - for _, xf := range s.exporters { - mayForce = mayForce || xf(t) - } - mayForceInit = true - } - step.mayForce = mayForce - step.paddr = addr - step.pvx = vax - step.pvy = vay - step.field = t.Field(i) - } - s.compareAny(step) - } -} - -func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { - isSlice := t.Kind() == reflect.Slice - if isSlice && (vx.IsNil() || vy.IsNil()) { - s.report(vx.IsNil() && vy.IsNil(), 0) - return - } - - // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer - // since slices represents a list of pointers, rather than a single pointer. - // The pointer checking logic must be handled on a per-element basis - // in compareAny. - // - // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting - // pointer P, a length N, and a capacity C. Supposing each slice element has - // a memory size of M, then the slice is equivalent to the list of pointers: - // [P+i*M for i in range(N)] - // - // For example, v[:0] and v[:1] are slices with the same starting pointer, - // but they are clearly different values. Using the slice pointer alone - // violates the assumption that equal pointers implies equal values. - - step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} - withIndexes := func(ix, iy int) SliceIndex { - if ix >= 0 { - step.vx, step.xkey = vx.Index(ix), ix - } else { - step.vx, step.xkey = reflect.Value{}, -1 - } - if iy >= 0 { - step.vy, step.ykey = vy.Index(iy), iy - } else { - step.vy, step.ykey = reflect.Value{}, -1 - } - return step - } - - // Ignore options are able to ignore missing elements in a slice. - // However, detecting these reliably requires an optimal differencing - // algorithm, for which diff.Difference is not. - // - // Instead, we first iterate through both slices to detect which elements - // would be ignored if standing alone. The index of non-discarded elements - // are stored in a separate slice, which diffing is then performed on. - var indexesX, indexesY []int - var ignoredX, ignoredY []bool - for ix := 0; ix < vx.Len(); ix++ { - ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 - if !ignored { - indexesX = append(indexesX, ix) - } - ignoredX = append(ignoredX, ignored) - } - for iy := 0; iy < vy.Len(); iy++ { - ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 - if !ignored { - indexesY = append(indexesY, iy) - } - ignoredY = append(ignoredY, ignored) - } - - // Compute an edit-script for slices vx and vy (excluding ignored elements). - edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { - return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) - }) - - // Replay the ignore-scripts and the edit-script. - var ix, iy int - for ix < vx.Len() || iy < vy.Len() { - var e diff.EditType - switch { - case ix < len(ignoredX) && ignoredX[ix]: - e = diff.UniqueX - case iy < len(ignoredY) && ignoredY[iy]: - e = diff.UniqueY - default: - e, edits = edits[0], edits[1:] - } - switch e { - case diff.UniqueX: - s.compareAny(withIndexes(ix, -1)) - ix++ - case diff.UniqueY: - s.compareAny(withIndexes(-1, iy)) - iy++ - default: - s.compareAny(withIndexes(ix, iy)) - ix++ - iy++ - } - } -} - -func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { - if vx.IsNil() || vy.IsNil() { - s.report(vx.IsNil() && vy.IsNil(), 0) - return - } - - // Cycle-detection for maps. - if eq, visited := s.curPtrs.Push(vx, vy); visited { - s.report(eq, reportByCycle) - return - } - defer s.curPtrs.Pop(vx, vy) - - // We combine and sort the two map keys so that we can perform the - // comparisons in a deterministic order. - step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} - for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { - step.vx = vx.MapIndex(k) - step.vy = vy.MapIndex(k) - step.key = k - if !step.vx.IsValid() && !step.vy.IsValid() { - // It is possible for both vx and vy to be invalid if the - // key contained a NaN value in it. - // - // Even with the ability to retrieve NaN keys in Go 1.12, - // there still isn't a sensible way to compare the values since - // a NaN key may map to multiple unordered values. - // The most reasonable way to compare NaNs would be to compare the - // set of values. However, this is impossible to do efficiently - // since set equality is provably an O(n^2) operation given only - // an Equal function. If we had a Less function or Hash function, - // this could be done in O(n*log(n)) or O(n), respectively. - // - // Rather than adding complex logic to deal with NaNs, make it - // the user's responsibility to compare such obscure maps. - const help = "consider providing a Comparer to compare the map" - panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) - } - s.compareAny(step) - } -} - -func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { - if vx.IsNil() || vy.IsNil() { - s.report(vx.IsNil() && vy.IsNil(), 0) - return - } - - // Cycle-detection for pointers. - if eq, visited := s.curPtrs.Push(vx, vy); visited { - s.report(eq, reportByCycle) - return - } - defer s.curPtrs.Pop(vx, vy) - - vx, vy = vx.Elem(), vy.Elem() - s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) -} - -func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { - if vx.IsNil() || vy.IsNil() { - s.report(vx.IsNil() && vy.IsNil(), 0) - return - } - vx, vy = vx.Elem(), vy.Elem() - if vx.Type() != vy.Type() { - s.report(false, 0) - return - } - s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) -} - -func (s *state) report(eq bool, rf resultFlags) { - if rf&reportByIgnore == 0 { - if eq { - s.result.NumSame++ - rf |= reportEqual - } else { - s.result.NumDiff++ - rf |= reportUnequal - } - } - for _, r := range s.reporters { - r.Report(Result{flags: rf}) - } -} - -// recChecker tracks the state needed to periodically perform checks that -// user provided transformers are not stuck in an infinitely recursive cycle. -type recChecker struct{ next int } - -// Check scans the Path for any recursive transformers and panics when any -// recursive transformers are detected. Note that the presence of a -// recursive Transformer does not necessarily imply an infinite cycle. -// As such, this check only activates after some minimal number of path steps. -func (rc *recChecker) Check(p Path) { - const minLen = 1 << 16 - if rc.next == 0 { - rc.next = minLen - } - if len(p) < rc.next { - return - } - rc.next <<= 1 - - // Check whether the same transformer has appeared at least twice. - var ss []string - m := map[Option]int{} - for _, ps := range p { - if t, ok := ps.(Transform); ok { - t := t.Option() - if m[t] == 1 { // Transformer was used exactly once before - tf := t.(*transformer).fnc.Type() - ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) - } - m[t]++ - } - } - if len(ss) > 0 { - const warning = "recursive set of Transformers detected" - const help = "consider using cmpopts.AcyclicTransformer" - set := strings.Join(ss, "\n\t") - panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) - } -} - -// dynChecker tracks the state needed to periodically perform checks that -// user provided functions are symmetric and deterministic. -// The zero value is safe for immediate use. -type dynChecker struct{ curr, next int } - -// Next increments the state and reports whether a check should be performed. -// -// Checks occur every Nth function call, where N is a triangular number: -// -// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... -// -// See https://en.wikipedia.org/wiki/Triangular_number -// -// This sequence ensures that the cost of checks drops significantly as -// the number of functions calls grows larger. -func (dc *dynChecker) Next() bool { - ok := dc.curr == dc.next - if ok { - dc.curr = 0 - dc.next++ - } - dc.curr++ - return ok -} - -// makeAddressable returns a value that is always addressable. -// It returns the input verbatim if it is already addressable, -// otherwise it creates a new value and returns an addressable copy. -func makeAddressable(v reflect.Value) reflect.Value { - if v.CanAddr() { - return v - } - vc := reflect.New(v.Type()).Elem() - vc.Set(v) - return vc -} diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go deleted file mode 100644 index ae851fe5..00000000 --- a/vendor/github.com/google/go-cmp/cmp/export_panic.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build purego -// +build purego - -package cmp - -import "reflect" - -const supportExporters = false - -func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { - panic("no support for forcibly accessing unexported fields") -} diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go deleted file mode 100644 index e2c0f74e..00000000 --- a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !purego -// +build !purego - -package cmp - -import ( - "reflect" - "unsafe" -) - -const supportExporters = true - -// retrieveUnexportedField uses unsafe to forcibly retrieve any field from -// a struct such that the value has read-write permissions. -// -// The parent struct, v, must be addressable, while f must be a StructField -// describing the field to retrieve. If addr is false, -// then the returned value will be shallowed copied to be non-addressable. -func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { - ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() - if !addr { - // A field is addressable if and only if the struct is addressable. - // If the original parent value was not addressable, shallow copy the - // value to make it non-addressable to avoid leaking an implementation - // detail of how forcibly exporting a field works. - if ve.Kind() == reflect.Interface && ve.IsNil() { - return reflect.Zero(f.Type) - } - return reflect.ValueOf(ve.Interface()).Convert(f.Type) - } - return ve -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go deleted file mode 100644 index 36062a60..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !cmp_debug -// +build !cmp_debug - -package diff - -var debug debugger - -type debugger struct{} - -func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { - return f -} -func (debugger) Update() {} -func (debugger) Finish() {} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go deleted file mode 100644 index a3b97a1a..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build cmp_debug -// +build cmp_debug - -package diff - -import ( - "fmt" - "strings" - "sync" - "time" -) - -// The algorithm can be seen running in real-time by enabling debugging: -// go test -tags=cmp_debug -v -// -// Example output: -// === RUN TestDifference/#34 -// ┌───────────────────────────────┐ -// │ \ · · · · · · · · · · · · · · │ -// │ · # · · · · · · · · · · · · · │ -// │ · \ · · · · · · · · · · · · · │ -// │ · · \ · · · · · · · · · · · · │ -// │ · · · X # · · · · · · · · · · │ -// │ · · · # \ · · · · · · · · · · │ -// │ · · · · · # # · · · · · · · · │ -// │ · · · · · # \ · · · · · · · · │ -// │ · · · · · · · \ · · · · · · · │ -// │ · · · · · · · · \ · · · · · · │ -// │ · · · · · · · · · \ · · · · · │ -// │ · · · · · · · · · · \ · · # · │ -// │ · · · · · · · · · · · \ # # · │ -// │ · · · · · · · · · · · # # # · │ -// │ · · · · · · · · · · # # # # · │ -// │ · · · · · · · · · # # # # # · │ -// │ · · · · · · · · · · · · · · \ │ -// └───────────────────────────────┘ -// [.Y..M.XY......YXYXY.|] -// -// The grid represents the edit-graph where the horizontal axis represents -// list X and the vertical axis represents list Y. The start of the two lists -// is the top-left, while the ends are the bottom-right. The '·' represents -// an unexplored node in the graph. The '\' indicates that the two symbols -// from list X and Y are equal. The 'X' indicates that two symbols are similar -// (but not exactly equal) to each other. The '#' indicates that the two symbols -// are different (and not similar). The algorithm traverses this graph trying to -// make the paths starting in the top-left and the bottom-right connect. -// -// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents -// the currently established path from the forward and reverse searches, -// separated by a '|' character. - -const ( - updateDelay = 100 * time.Millisecond - finishDelay = 500 * time.Millisecond - ansiTerminal = true // ANSI escape codes used to move terminal cursor -) - -var debug debugger - -type debugger struct { - sync.Mutex - p1, p2 EditScript - fwdPath, revPath *EditScript - grid []byte - lines int -} - -func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { - dbg.Lock() - dbg.fwdPath, dbg.revPath = p1, p2 - top := "┌─" + strings.Repeat("──", nx) + "┐\n" - row := "│ " + strings.Repeat("· ", nx) + "│\n" - btm := "└─" + strings.Repeat("──", nx) + "┘\n" - dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) - dbg.lines = strings.Count(dbg.String(), "\n") - fmt.Print(dbg) - - // Wrap the EqualFunc so that we can intercept each result. - return func(ix, iy int) (r Result) { - cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] - for i := range cell { - cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot - } - switch r = f(ix, iy); { - case r.Equal(): - cell[0] = '\\' - case r.Similar(): - cell[0] = 'X' - default: - cell[0] = '#' - } - return - } -} - -func (dbg *debugger) Update() { - dbg.print(updateDelay) -} - -func (dbg *debugger) Finish() { - dbg.print(finishDelay) - dbg.Unlock() -} - -func (dbg *debugger) String() string { - dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] - for i := len(*dbg.revPath) - 1; i >= 0; i-- { - dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) - } - return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) -} - -func (dbg *debugger) print(d time.Duration) { - if ansiTerminal { - fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor - } - fmt.Print(dbg) - time.Sleep(d) -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go deleted file mode 100644 index a248e543..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package diff implements an algorithm for producing edit-scripts. -// The edit-script is a sequence of operations needed to transform one list -// of symbols into another (or vice-versa). The edits allowed are insertions, -// deletions, and modifications. The summation of all edits is called the -// Levenshtein distance as this problem is well-known in computer science. -// -// This package prioritizes performance over accuracy. That is, the run time -// is more important than obtaining a minimal Levenshtein distance. -package diff - -import ( - "math/rand" - "time" - - "github.com/google/go-cmp/cmp/internal/flags" -) - -// EditType represents a single operation within an edit-script. -type EditType uint8 - -const ( - // Identity indicates that a symbol pair is identical in both list X and Y. - Identity EditType = iota - // UniqueX indicates that a symbol only exists in X and not Y. - UniqueX - // UniqueY indicates that a symbol only exists in Y and not X. - UniqueY - // Modified indicates that a symbol pair is a modification of each other. - Modified -) - -// EditScript represents the series of differences between two lists. -type EditScript []EditType - -// String returns a human-readable string representing the edit-script where -// Identity, UniqueX, UniqueY, and Modified are represented by the -// '.', 'X', 'Y', and 'M' characters, respectively. -func (es EditScript) String() string { - b := make([]byte, len(es)) - for i, e := range es { - switch e { - case Identity: - b[i] = '.' - case UniqueX: - b[i] = 'X' - case UniqueY: - b[i] = 'Y' - case Modified: - b[i] = 'M' - default: - panic("invalid edit-type") - } - } - return string(b) -} - -// stats returns a histogram of the number of each type of edit operation. -func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { - for _, e := range es { - switch e { - case Identity: - s.NI++ - case UniqueX: - s.NX++ - case UniqueY: - s.NY++ - case Modified: - s.NM++ - default: - panic("invalid edit-type") - } - } - return -} - -// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if -// lists X and Y are equal. -func (es EditScript) Dist() int { return len(es) - es.stats().NI } - -// LenX is the length of the X list. -func (es EditScript) LenX() int { return len(es) - es.stats().NY } - -// LenY is the length of the Y list. -func (es EditScript) LenY() int { return len(es) - es.stats().NX } - -// EqualFunc reports whether the symbols at indexes ix and iy are equal. -// When called by Difference, the index is guaranteed to be within nx and ny. -type EqualFunc func(ix int, iy int) Result - -// Result is the result of comparison. -// NumSame is the number of sub-elements that are equal. -// NumDiff is the number of sub-elements that are not equal. -type Result struct{ NumSame, NumDiff int } - -// BoolResult returns a Result that is either Equal or not Equal. -func BoolResult(b bool) Result { - if b { - return Result{NumSame: 1} // Equal, Similar - } else { - return Result{NumDiff: 2} // Not Equal, not Similar - } -} - -// Equal indicates whether the symbols are equal. Two symbols are equal -// if and only if NumDiff == 0. If Equal, then they are also Similar. -func (r Result) Equal() bool { return r.NumDiff == 0 } - -// Similar indicates whether two symbols are similar and may be represented -// by using the Modified type. As a special case, we consider binary comparisons -// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. -// -// The exact ratio of NumSame to NumDiff to determine similarity may change. -func (r Result) Similar() bool { - // Use NumSame+1 to offset NumSame so that binary comparisons are similar. - return r.NumSame+1 >= r.NumDiff -} - -var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 - -// Difference reports whether two lists of lengths nx and ny are equal -// given the definition of equality provided as f. -// -// This function returns an edit-script, which is a sequence of operations -// needed to convert one list into the other. The following invariants for -// the edit-script are maintained: -// - eq == (es.Dist()==0) -// - nx == es.LenX() -// - ny == es.LenY() -// -// This algorithm is not guaranteed to be an optimal solution (i.e., one that -// produces an edit-script with a minimal Levenshtein distance). This algorithm -// favors performance over optimality. The exact output is not guaranteed to -// be stable and may change over time. -func Difference(nx, ny int, f EqualFunc) (es EditScript) { - // This algorithm is based on traversing what is known as an "edit-graph". - // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" - // by Eugene W. Myers. Since D can be as large as N itself, this is - // effectively O(N^2). Unlike the algorithm from that paper, we are not - // interested in the optimal path, but at least some "decent" path. - // - // For example, let X and Y be lists of symbols: - // X = [A B C A B B A] - // Y = [C B A B A C] - // - // The edit-graph can be drawn as the following: - // A B C A B B A - // ┌─────────────┐ - // C │_|_|\|_|_|_|_│ 0 - // B │_|\|_|_|\|\|_│ 1 - // A │\|_|_|\|_|_|\│ 2 - // B │_|\|_|_|\|\|_│ 3 - // A │\|_|_|\|_|_|\│ 4 - // C │ | |\| | | | │ 5 - // └─────────────┘ 6 - // 0 1 2 3 4 5 6 7 - // - // List X is written along the horizontal axis, while list Y is written - // along the vertical axis. At any point on this grid, if the symbol in - // list X matches the corresponding symbol in list Y, then a '\' is drawn. - // The goal of any minimal edit-script algorithm is to find a path from the - // top-left corner to the bottom-right corner, while traveling through the - // fewest horizontal or vertical edges. - // A horizontal edge is equivalent to inserting a symbol from list X. - // A vertical edge is equivalent to inserting a symbol from list Y. - // A diagonal edge is equivalent to a matching symbol between both X and Y. - - // Invariants: - // - 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx - // - 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny - // - // In general: - // - fwdFrontier.X < revFrontier.X - // - fwdFrontier.Y < revFrontier.Y - // - // Unless, it is time for the algorithm to terminate. - fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} - revPath := path{-1, point{nx, ny}, make(EditScript, 0)} - fwdFrontier := fwdPath.point // Forward search frontier - revFrontier := revPath.point // Reverse search frontier - - // Search budget bounds the cost of searching for better paths. - // The longest sequence of non-matching symbols that can be tolerated is - // approximately the square-root of the search budget. - searchBudget := 4 * (nx + ny) // O(n) - - // Running the tests with the "cmp_debug" build tag prints a visualization - // of the algorithm running in real-time. This is educational for - // understanding how the algorithm works. See debug_enable.go. - f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) - - // The algorithm below is a greedy, meet-in-the-middle algorithm for - // computing sub-optimal edit-scripts between two lists. - // - // The algorithm is approximately as follows: - // - Searching for differences switches back-and-forth between - // a search that starts at the beginning (the top-left corner), and - // a search that starts at the end (the bottom-right corner). - // The goal of the search is connect with the search - // from the opposite corner. - // - As we search, we build a path in a greedy manner, - // where the first match seen is added to the path (this is sub-optimal, - // but provides a decent result in practice). When matches are found, - // we try the next pair of symbols in the lists and follow all matches - // as far as possible. - // - When searching for matches, we search along a diagonal going through - // through the "frontier" point. If no matches are found, - // we advance the frontier towards the opposite corner. - // - This algorithm terminates when either the X coordinates or the - // Y coordinates of the forward and reverse frontier points ever intersect. - - // This algorithm is correct even if searching only in the forward direction - // or in the reverse direction. We do both because it is commonly observed - // that two lists commonly differ because elements were added to the front - // or end of the other list. - // - // Non-deterministically start with either the forward or reverse direction - // to introduce some deliberate instability so that we have the flexibility - // to change this algorithm in the future. - if flags.Deterministic || randBool { - goto forwardSearch - } else { - goto reverseSearch - } - -forwardSearch: - { - // Forward search from the beginning. - if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { - goto finishSearch - } - for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { - // Search in a diagonal pattern for a match. - z := zigzag(i) - p := point{fwdFrontier.X + z, fwdFrontier.Y - z} - switch { - case p.X >= revPath.X || p.Y < fwdPath.Y: - stop1 = true // Hit top-right corner - case p.Y >= revPath.Y || p.X < fwdPath.X: - stop2 = true // Hit bottom-left corner - case f(p.X, p.Y).Equal(): - // Match found, so connect the path to this point. - fwdPath.connect(p, f) - fwdPath.append(Identity) - // Follow sequence of matches as far as possible. - for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { - if !f(fwdPath.X, fwdPath.Y).Equal() { - break - } - fwdPath.append(Identity) - } - fwdFrontier = fwdPath.point - stop1, stop2 = true, true - default: - searchBudget-- // Match not found - } - debug.Update() - } - // Advance the frontier towards reverse point. - if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { - fwdFrontier.X++ - } else { - fwdFrontier.Y++ - } - goto reverseSearch - } - -reverseSearch: - { - // Reverse search from the end. - if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { - goto finishSearch - } - for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { - // Search in a diagonal pattern for a match. - z := zigzag(i) - p := point{revFrontier.X - z, revFrontier.Y + z} - switch { - case fwdPath.X >= p.X || revPath.Y < p.Y: - stop1 = true // Hit bottom-left corner - case fwdPath.Y >= p.Y || revPath.X < p.X: - stop2 = true // Hit top-right corner - case f(p.X-1, p.Y-1).Equal(): - // Match found, so connect the path to this point. - revPath.connect(p, f) - revPath.append(Identity) - // Follow sequence of matches as far as possible. - for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { - if !f(revPath.X-1, revPath.Y-1).Equal() { - break - } - revPath.append(Identity) - } - revFrontier = revPath.point - stop1, stop2 = true, true - default: - searchBudget-- // Match not found - } - debug.Update() - } - // Advance the frontier towards forward point. - if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { - revFrontier.X-- - } else { - revFrontier.Y-- - } - goto forwardSearch - } - -finishSearch: - // Join the forward and reverse paths and then append the reverse path. - fwdPath.connect(revPath.point, f) - for i := len(revPath.es) - 1; i >= 0; i-- { - t := revPath.es[i] - revPath.es = revPath.es[:i] - fwdPath.append(t) - } - debug.Finish() - return fwdPath.es -} - -type path struct { - dir int // +1 if forward, -1 if reverse - point // Leading point of the EditScript path - es EditScript -} - -// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types -// to the edit-script to connect p.point to dst. -func (p *path) connect(dst point, f EqualFunc) { - if p.dir > 0 { - // Connect in forward direction. - for dst.X > p.X && dst.Y > p.Y { - switch r := f(p.X, p.Y); { - case r.Equal(): - p.append(Identity) - case r.Similar(): - p.append(Modified) - case dst.X-p.X >= dst.Y-p.Y: - p.append(UniqueX) - default: - p.append(UniqueY) - } - } - for dst.X > p.X { - p.append(UniqueX) - } - for dst.Y > p.Y { - p.append(UniqueY) - } - } else { - // Connect in reverse direction. - for p.X > dst.X && p.Y > dst.Y { - switch r := f(p.X-1, p.Y-1); { - case r.Equal(): - p.append(Identity) - case r.Similar(): - p.append(Modified) - case p.Y-dst.Y >= p.X-dst.X: - p.append(UniqueY) - default: - p.append(UniqueX) - } - } - for p.X > dst.X { - p.append(UniqueX) - } - for p.Y > dst.Y { - p.append(UniqueY) - } - } -} - -func (p *path) append(t EditType) { - p.es = append(p.es, t) - switch t { - case Identity, Modified: - p.add(p.dir, p.dir) - case UniqueX: - p.add(p.dir, 0) - case UniqueY: - p.add(0, p.dir) - } - debug.Update() -} - -type point struct{ X, Y int } - -func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } - -// zigzag maps a consecutive sequence of integers to a zig-zag sequence. -// -// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] -func zigzag(x int) int { - if x&1 != 0 { - x = ^x - } - return x >> 1 -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go deleted file mode 100644 index d8e459c9..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package flags - -// Deterministic controls whether the output of Diff should be deterministic. -// This is only used for testing. -var Deterministic bool diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go deleted file mode 100644 index d127d436..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package function provides functionality for identifying function types. -package function - -import ( - "reflect" - "regexp" - "runtime" - "strings" -) - -type funcType int - -const ( - _ funcType = iota - - tbFunc // func(T) bool - ttbFunc // func(T, T) bool - trbFunc // func(T, R) bool - tibFunc // func(T, I) bool - trFunc // func(T) R - - Equal = ttbFunc // func(T, T) bool - EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool - Transformer = trFunc // func(T) R - ValueFilter = ttbFunc // func(T, T) bool - Less = ttbFunc // func(T, T) bool - ValuePredicate = tbFunc // func(T) bool - KeyValuePredicate = trbFunc // func(T, R) bool -) - -var boolType = reflect.TypeOf(true) - -// IsType reports whether the reflect.Type is of the specified function type. -func IsType(t reflect.Type, ft funcType) bool { - if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { - return false - } - ni, no := t.NumIn(), t.NumOut() - switch ft { - case tbFunc: // func(T) bool - if ni == 1 && no == 1 && t.Out(0) == boolType { - return true - } - case ttbFunc: // func(T, T) bool - if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { - return true - } - case trbFunc: // func(T, R) bool - if ni == 2 && no == 1 && t.Out(0) == boolType { - return true - } - case tibFunc: // func(T, I) bool - if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { - return true - } - case trFunc: // func(T) R - if ni == 1 && no == 1 { - return true - } - } - return false -} - -var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) - -// NameOf returns the name of the function value. -func NameOf(v reflect.Value) string { - fnc := runtime.FuncForPC(v.Pointer()) - if fnc == nil { - return "" - } - fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" - - // Method closures have a "-fm" suffix. - fullName = strings.TrimSuffix(fullName, "-fm") - - var name string - for len(fullName) > 0 { - inParen := strings.HasSuffix(fullName, ")") - fullName = strings.TrimSuffix(fullName, ")") - - s := lastIdentRx.FindString(fullName) - if s == "" { - break - } - name = s + "." + name - fullName = strings.TrimSuffix(fullName, s) - - if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { - fullName = fullName[:i] - } - fullName = strings.TrimSuffix(fullName, ".") - } - return strings.TrimSuffix(name, ".") -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/name.go b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go deleted file mode 100644 index 7b498bb2..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/name.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2020, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package value - -import ( - "reflect" - "strconv" -) - -var anyType = reflect.TypeOf((*interface{})(nil)).Elem() - -// TypeString is nearly identical to reflect.Type.String, -// but has an additional option to specify that full type names be used. -func TypeString(t reflect.Type, qualified bool) string { - return string(appendTypeName(nil, t, qualified, false)) -} - -func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { - // BUG: Go reflection provides no way to disambiguate two named types - // of the same name and within the same package, - // but declared within the namespace of different functions. - - // Use the "any" alias instead of "interface{}" for better readability. - if t == anyType { - return append(b, "any"...) - } - - // Named type. - if t.Name() != "" { - if qualified && t.PkgPath() != "" { - b = append(b, '"') - b = append(b, t.PkgPath()...) - b = append(b, '"') - b = append(b, '.') - b = append(b, t.Name()...) - } else { - b = append(b, t.String()...) - } - return b - } - - // Unnamed type. - switch k := t.Kind(); k { - case reflect.Bool, reflect.String, reflect.UnsafePointer, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: - b = append(b, k.String()...) - case reflect.Chan: - if t.ChanDir() == reflect.RecvDir { - b = append(b, "<-"...) - } - b = append(b, "chan"...) - if t.ChanDir() == reflect.SendDir { - b = append(b, "<-"...) - } - b = append(b, ' ') - b = appendTypeName(b, t.Elem(), qualified, false) - case reflect.Func: - if !elideFunc { - b = append(b, "func"...) - } - b = append(b, '(') - for i := 0; i < t.NumIn(); i++ { - if i > 0 { - b = append(b, ", "...) - } - if i == t.NumIn()-1 && t.IsVariadic() { - b = append(b, "..."...) - b = appendTypeName(b, t.In(i).Elem(), qualified, false) - } else { - b = appendTypeName(b, t.In(i), qualified, false) - } - } - b = append(b, ')') - switch t.NumOut() { - case 0: - // Do nothing - case 1: - b = append(b, ' ') - b = appendTypeName(b, t.Out(0), qualified, false) - default: - b = append(b, " ("...) - for i := 0; i < t.NumOut(); i++ { - if i > 0 { - b = append(b, ", "...) - } - b = appendTypeName(b, t.Out(i), qualified, false) - } - b = append(b, ')') - } - case reflect.Struct: - b = append(b, "struct{ "...) - for i := 0; i < t.NumField(); i++ { - if i > 0 { - b = append(b, "; "...) - } - sf := t.Field(i) - if !sf.Anonymous { - if qualified && sf.PkgPath != "" { - b = append(b, '"') - b = append(b, sf.PkgPath...) - b = append(b, '"') - b = append(b, '.') - } - b = append(b, sf.Name...) - b = append(b, ' ') - } - b = appendTypeName(b, sf.Type, qualified, false) - if sf.Tag != "" { - b = append(b, ' ') - b = strconv.AppendQuote(b, string(sf.Tag)) - } - } - if b[len(b)-1] == ' ' { - b = b[:len(b)-1] - } else { - b = append(b, ' ') - } - b = append(b, '}') - case reflect.Slice, reflect.Array: - b = append(b, '[') - if k == reflect.Array { - b = strconv.AppendUint(b, uint64(t.Len()), 10) - } - b = append(b, ']') - b = appendTypeName(b, t.Elem(), qualified, false) - case reflect.Map: - b = append(b, "map["...) - b = appendTypeName(b, t.Key(), qualified, false) - b = append(b, ']') - b = appendTypeName(b, t.Elem(), qualified, false) - case reflect.Ptr: - b = append(b, '*') - b = appendTypeName(b, t.Elem(), qualified, false) - case reflect.Interface: - b = append(b, "interface{ "...) - for i := 0; i < t.NumMethod(); i++ { - if i > 0 { - b = append(b, "; "...) - } - m := t.Method(i) - if qualified && m.PkgPath != "" { - b = append(b, '"') - b = append(b, m.PkgPath...) - b = append(b, '"') - b = append(b, '.') - } - b = append(b, m.Name...) - b = appendTypeName(b, m.Type, qualified, true) - } - if b[len(b)-1] == ' ' { - b = b[:len(b)-1] - } else { - b = append(b, ' ') - } - b = append(b, '}') - default: - panic("invalid kind: " + k.String()) - } - return b -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go deleted file mode 100644 index 1a71bfcb..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build purego -// +build purego - -package value - -import "reflect" - -// Pointer is an opaque typed pointer and is guaranteed to be comparable. -type Pointer struct { - p uintptr - t reflect.Type -} - -// PointerOf returns a Pointer from v, which must be a -// reflect.Ptr, reflect.Slice, or reflect.Map. -func PointerOf(v reflect.Value) Pointer { - // NOTE: Storing a pointer as an uintptr is technically incorrect as it - // assumes that the GC implementation does not use a moving collector. - return Pointer{v.Pointer(), v.Type()} -} - -// IsNil reports whether the pointer is nil. -func (p Pointer) IsNil() bool { - return p.p == 0 -} - -// Uintptr returns the pointer as a uintptr. -func (p Pointer) Uintptr() uintptr { - return p.p -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go deleted file mode 100644 index 16e6860a..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !purego -// +build !purego - -package value - -import ( - "reflect" - "unsafe" -) - -// Pointer is an opaque typed pointer and is guaranteed to be comparable. -type Pointer struct { - p unsafe.Pointer - t reflect.Type -} - -// PointerOf returns a Pointer from v, which must be a -// reflect.Ptr, reflect.Slice, or reflect.Map. -func PointerOf(v reflect.Value) Pointer { - // The proper representation of a pointer is unsafe.Pointer, - // which is necessary if the GC ever uses a moving collector. - return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} -} - -// IsNil reports whether the pointer is nil. -func (p Pointer) IsNil() bool { - return p.p == nil -} - -// Uintptr returns the pointer as a uintptr. -func (p Pointer) Uintptr() uintptr { - return uintptr(p.p) -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go deleted file mode 100644 index 98533b03..00000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package value - -import ( - "fmt" - "math" - "reflect" - "sort" -) - -// SortKeys sorts a list of map keys, deduplicating keys if necessary. -// The type of each value must be comparable. -func SortKeys(vs []reflect.Value) []reflect.Value { - if len(vs) == 0 { - return vs - } - - // Sort the map keys. - sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) - - // Deduplicate keys (fails for NaNs). - vs2 := vs[:1] - for _, v := range vs[1:] { - if isLess(vs2[len(vs2)-1], v) { - vs2 = append(vs2, v) - } - } - return vs2 -} - -// isLess is a generic function for sorting arbitrary map keys. -// The inputs must be of the same type and must be comparable. -func isLess(x, y reflect.Value) bool { - switch x.Type().Kind() { - case reflect.Bool: - return !x.Bool() && y.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return x.Int() < y.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return x.Uint() < y.Uint() - case reflect.Float32, reflect.Float64: - // NOTE: This does not sort -0 as less than +0 - // since Go maps treat -0 and +0 as equal keys. - fx, fy := x.Float(), y.Float() - return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) - case reflect.Complex64, reflect.Complex128: - cx, cy := x.Complex(), y.Complex() - rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) - if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { - return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) - } - return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) - case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: - return x.Pointer() < y.Pointer() - case reflect.String: - return x.String() < y.String() - case reflect.Array: - for i := 0; i < x.Len(); i++ { - if isLess(x.Index(i), y.Index(i)) { - return true - } - if isLess(y.Index(i), x.Index(i)) { - return false - } - } - return false - case reflect.Struct: - for i := 0; i < x.NumField(); i++ { - if isLess(x.Field(i), y.Field(i)) { - return true - } - if isLess(y.Field(i), x.Field(i)) { - return false - } - } - return false - case reflect.Interface: - vx, vy := x.Elem(), y.Elem() - if !vx.IsValid() || !vy.IsValid() { - return !vx.IsValid() && vy.IsValid() - } - tx, ty := vx.Type(), vy.Type() - if tx == ty { - return isLess(x.Elem(), y.Elem()) - } - if tx.Kind() != ty.Kind() { - return vx.Kind() < vy.Kind() - } - if tx.String() != ty.String() { - return tx.String() < ty.String() - } - if tx.PkgPath() != ty.PkgPath() { - return tx.PkgPath() < ty.PkgPath() - } - // This can happen in rare situations, so we fallback to just comparing - // the unique pointer for a reflect.Type. This guarantees deterministic - // ordering within a program, but it is obviously not stable. - return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() - default: - // Must be Func, Map, or Slice; which are not comparable. - panic(fmt.Sprintf("%T is not comparable", x.Type())) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go deleted file mode 100644 index 1f9ca9c4..00000000 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "fmt" - "reflect" - "regexp" - "strings" - - "github.com/google/go-cmp/cmp/internal/function" -) - -// Option configures for specific behavior of Equal and Diff. In particular, -// the fundamental Option functions (Ignore, Transformer, and Comparer), -// configure how equality is determined. -// -// The fundamental options may be composed with filters (FilterPath and -// FilterValues) to control the scope over which they are applied. -// -// The cmp/cmpopts package provides helper functions for creating options that -// may be used with Equal and Diff. -type Option interface { - // filter applies all filters and returns the option that remains. - // Each option may only read s.curPath and call s.callTTBFunc. - // - // An Options is returned only if multiple comparers or transformers - // can apply simultaneously and will only contain values of those types - // or sub-Options containing values of those types. - filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption -} - -// applicableOption represents the following types: -// -// Fundamental: ignore | validator | *comparer | *transformer -// Grouping: Options -type applicableOption interface { - Option - - // apply executes the option, which may mutate s or panic. - apply(s *state, vx, vy reflect.Value) -} - -// coreOption represents the following types: -// -// Fundamental: ignore | validator | *comparer | *transformer -// Filters: *pathFilter | *valuesFilter -type coreOption interface { - Option - isCore() -} - -type core struct{} - -func (core) isCore() {} - -// Options is a list of Option values that also satisfies the Option interface. -// Helper comparison packages may return an Options value when packing multiple -// Option values into a single Option. When this package processes an Options, -// it will be implicitly expanded into a flat list. -// -// Applying a filter on an Options is equivalent to applying that same filter -// on all individual options held within. -type Options []Option - -func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { - for _, opt := range opts { - switch opt := opt.filter(s, t, vx, vy); opt.(type) { - case ignore: - return ignore{} // Only ignore can short-circuit evaluation - case validator: - out = validator{} // Takes precedence over comparer or transformer - case *comparer, *transformer, Options: - switch out.(type) { - case nil: - out = opt - case validator: - // Keep validator - case *comparer, *transformer, Options: - out = Options{out, opt} // Conflicting comparers or transformers - } - } - } - return out -} - -func (opts Options) apply(s *state, _, _ reflect.Value) { - const warning = "ambiguous set of applicable options" - const help = "consider using filters to ensure at most one Comparer or Transformer may apply" - var ss []string - for _, opt := range flattenOptions(nil, opts) { - ss = append(ss, fmt.Sprint(opt)) - } - set := strings.Join(ss, "\n\t") - panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) -} - -func (opts Options) String() string { - var ss []string - for _, opt := range opts { - ss = append(ss, fmt.Sprint(opt)) - } - return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) -} - -// FilterPath returns a new Option where opt is only evaluated if filter f -// returns true for the current Path in the value tree. -// -// This filter is called even if a slice element or map entry is missing and -// provides an opportunity to ignore such cases. The filter function must be -// symmetric such that the filter result is identical regardless of whether the -// missing value is from x or y. -// -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. -func FilterPath(f func(Path) bool, opt Option) Option { - if f == nil { - panic("invalid path filter function") - } - if opt := normalizeOption(opt); opt != nil { - return &pathFilter{fnc: f, opt: opt} - } - return nil -} - -type pathFilter struct { - core - fnc func(Path) bool - opt Option -} - -func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { - if f.fnc(s.curPath) { - return f.opt.filter(s, t, vx, vy) - } - return nil -} - -func (f pathFilter) String() string { - return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) -} - -// FilterValues returns a new Option where opt is only evaluated if filter f, -// which is a function of the form "func(T, T) bool", returns true for the -// current pair of values being compared. If either value is invalid or -// the type of the values is not assignable to T, then this filter implicitly -// returns false. -// -// The filter function must be -// symmetric (i.e., agnostic to the order of the inputs) and -// deterministic (i.e., produces the same result when given the same inputs). -// If T is an interface, it is possible that f is called with two values with -// different concrete types that both implement T. -// -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. -func FilterValues(f interface{}, opt Option) Option { - v := reflect.ValueOf(f) - if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { - panic(fmt.Sprintf("invalid values filter function: %T", f)) - } - if opt := normalizeOption(opt); opt != nil { - vf := &valuesFilter{fnc: v, opt: opt} - if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { - vf.typ = ti - } - return vf - } - return nil -} - -type valuesFilter struct { - core - typ reflect.Type // T - fnc reflect.Value // func(T, T) bool - opt Option -} - -func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { - if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { - return nil - } - if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { - return f.opt.filter(s, t, vx, vy) - } - return nil -} - -func (f valuesFilter) String() string { - return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) -} - -// Ignore is an Option that causes all comparisons to be ignored. -// This value is intended to be combined with FilterPath or FilterValues. -// It is an error to pass an unfiltered Ignore option to Equal. -func Ignore() Option { return ignore{} } - -type ignore struct{ core } - -func (ignore) isFiltered() bool { return false } -func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } -func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } -func (ignore) String() string { return "Ignore()" } - -// validator is a sentinel Option type to indicate that some options could not -// be evaluated due to unexported fields, missing slice elements, or -// missing map entries. Both values are validator only for unexported fields. -type validator struct{ core } - -func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { - if !vx.IsValid() || !vy.IsValid() { - return validator{} - } - if !vx.CanInterface() || !vy.CanInterface() { - return validator{} - } - return nil -} -func (validator) apply(s *state, vx, vy reflect.Value) { - // Implies missing slice element or map entry. - if !vx.IsValid() || !vy.IsValid() { - s.report(vx.IsValid() == vy.IsValid(), 0) - return - } - - // Unable to Interface implies unexported field without visibility access. - if !vx.CanInterface() || !vy.CanInterface() { - help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" - var name string - if t := s.curPath.Index(-2).Type(); t.Name() != "" { - // Named type with unexported fields. - name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType - if _, ok := reflect.New(t).Interface().(error); ok { - help = "consider using cmpopts.EquateErrors to compare error values" - } - } else { - // Unnamed type with unexported fields. Derive PkgPath from field. - var pkgPath string - for i := 0; i < t.NumField() && pkgPath == ""; i++ { - pkgPath = t.Field(i).PkgPath - } - name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) - } - panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) - } - - panic("not reachable") -} - -// identRx represents a valid identifier according to the Go specification. -const identRx = `[_\p{L}][_\p{L}\p{N}]*` - -var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) - -// Transformer returns an Option that applies a transformation function that -// converts values of a certain type into that of another. -// -// The transformer f must be a function "func(T) R" that converts values of -// type T to those of type R and is implicitly filtered to input values -// assignable to T. The transformer must not mutate T in any way. -// -// To help prevent some cases of infinite recursive cycles applying the -// same transform to the output of itself (e.g., in the case where the -// input and output types are the same), an implicit filter is added such that -// a transformer is applicable only if that exact transformer is not already -// in the tail of the Path since the last non-Transform step. -// For situations where the implicit filter is still insufficient, -// consider using cmpopts.AcyclicTransformer, which adds a filter -// to prevent the transformer from being recursively applied upon itself. -// -// The name is a user provided label that is used as the Transform.Name in the -// transformation PathStep (and eventually shown in the Diff output). -// The name must be a valid identifier or qualified identifier in Go syntax. -// If empty, an arbitrary name is used. -func Transformer(name string, f interface{}) Option { - v := reflect.ValueOf(f) - if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { - panic(fmt.Sprintf("invalid transformer function: %T", f)) - } - if name == "" { - name = function.NameOf(v) - if !identsRx.MatchString(name) { - name = "λ" // Lambda-symbol as placeholder name - } - } else if !identsRx.MatchString(name) { - panic(fmt.Sprintf("invalid name: %q", name)) - } - tr := &transformer{name: name, fnc: reflect.ValueOf(f)} - if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { - tr.typ = ti - } - return tr -} - -type transformer struct { - core - name string - typ reflect.Type // T - fnc reflect.Value // func(T) R -} - -func (tr *transformer) isFiltered() bool { return tr.typ != nil } - -func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { - for i := len(s.curPath) - 1; i >= 0; i-- { - if t, ok := s.curPath[i].(Transform); !ok { - break // Hit most recent non-Transform step - } else if tr == t.trans { - return nil // Cannot directly use same Transform - } - } - if tr.typ == nil || t.AssignableTo(tr.typ) { - return tr - } - return nil -} - -func (tr *transformer) apply(s *state, vx, vy reflect.Value) { - step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} - vvx := s.callTRFunc(tr.fnc, vx, step) - vvy := s.callTRFunc(tr.fnc, vy, step) - step.vx, step.vy = vvx, vvy - s.compareAny(step) -} - -func (tr transformer) String() string { - return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) -} - -// Comparer returns an Option that determines whether two values are equal -// to each other. -// -// The comparer f must be a function "func(T, T) bool" and is implicitly -// filtered to input values assignable to T. If T is an interface, it is -// possible that f is called with two values of different concrete types that -// both implement T. -// -// The equality function must be: -// - Symmetric: equal(x, y) == equal(y, x) -// - Deterministic: equal(x, y) == equal(x, y) -// - Pure: equal(x, y) does not modify x or y -func Comparer(f interface{}) Option { - v := reflect.ValueOf(f) - if !function.IsType(v.Type(), function.Equal) || v.IsNil() { - panic(fmt.Sprintf("invalid comparer function: %T", f)) - } - cm := &comparer{fnc: v} - if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { - cm.typ = ti - } - return cm -} - -type comparer struct { - core - typ reflect.Type // T - fnc reflect.Value // func(T, T) bool -} - -func (cm *comparer) isFiltered() bool { return cm.typ != nil } - -func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { - if cm.typ == nil || t.AssignableTo(cm.typ) { - return cm - } - return nil -} - -func (cm *comparer) apply(s *state, vx, vy reflect.Value) { - eq := s.callTTBFunc(cm.fnc, vx, vy) - s.report(eq, reportByFunc) -} - -func (cm comparer) String() string { - return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) -} - -// Exporter returns an Option that specifies whether Equal is allowed to -// introspect into the unexported fields of certain struct types. -// -// Users of this option must understand that comparing on unexported fields -// from external packages is not safe since changes in the internal -// implementation of some external package may cause the result of Equal -// to unexpectedly change. However, it may be valid to use this option on types -// defined in an internal package where the semantic meaning of an unexported -// field is in the control of the user. -// -// In many cases, a custom Comparer should be used instead that defines -// equality as a function of the public API of a type rather than the underlying -// unexported implementation. -// -// For example, the reflect.Type documentation defines equality to be determined -// by the == operator on the interface (essentially performing a shallow pointer -// comparison) and most attempts to compare *regexp.Regexp types are interested -// in only checking that the regular expression strings are equal. -// Both of these are accomplished using Comparers: -// -// Comparer(func(x, y reflect.Type) bool { return x == y }) -// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) -// -// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore -// all unexported fields on specified struct types. -func Exporter(f func(reflect.Type) bool) Option { - if !supportExporters { - panic("Exporter is not supported on purego builds") - } - return exporter(f) -} - -type exporter func(reflect.Type) bool - -func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { - panic("not implemented") -} - -// AllowUnexported returns an Options that allows Equal to forcibly introspect -// unexported fields of the specified struct types. -// -// See Exporter for the proper use of this option. -func AllowUnexported(types ...interface{}) Option { - m := make(map[reflect.Type]bool) - for _, typ := range types { - t := reflect.TypeOf(typ) - if t.Kind() != reflect.Struct { - panic(fmt.Sprintf("invalid struct type: %T", typ)) - } - m[t] = true - } - return exporter(func(t reflect.Type) bool { return m[t] }) -} - -// Result represents the comparison result for a single node and -// is provided by cmp when calling Report (see Reporter). -type Result struct { - _ [0]func() // Make Result incomparable - flags resultFlags -} - -// Equal reports whether the node was determined to be equal or not. -// As a special case, ignored nodes are considered equal. -func (r Result) Equal() bool { - return r.flags&(reportEqual|reportByIgnore) != 0 -} - -// ByIgnore reports whether the node is equal because it was ignored. -// This never reports true if Equal reports false. -func (r Result) ByIgnore() bool { - return r.flags&reportByIgnore != 0 -} - -// ByMethod reports whether the Equal method determined equality. -func (r Result) ByMethod() bool { - return r.flags&reportByMethod != 0 -} - -// ByFunc reports whether a Comparer function determined equality. -func (r Result) ByFunc() bool { - return r.flags&reportByFunc != 0 -} - -// ByCycle reports whether a reference cycle was detected. -func (r Result) ByCycle() bool { - return r.flags&reportByCycle != 0 -} - -type resultFlags uint - -const ( - _ resultFlags = (1 << iota) / 2 - - reportEqual - reportUnequal - reportByIgnore - reportByMethod - reportByFunc - reportByCycle -) - -// Reporter is an Option that can be passed to Equal. When Equal traverses -// the value trees, it calls PushStep as it descends into each node in the -// tree and PopStep as it ascend out of the node. The leaves of the tree are -// either compared (determined to be equal or not equal) or ignored and reported -// as such by calling the Report method. -func Reporter(r interface { - // PushStep is called when a tree-traversal operation is performed. - // The PathStep itself is only valid until the step is popped. - // The PathStep.Values are valid for the duration of the entire traversal - // and must not be mutated. - // - // Equal always calls PushStep at the start to provide an operation-less - // PathStep used to report the root values. - // - // Within a slice, the exact set of inserted, removed, or modified elements - // is unspecified and may change in future implementations. - // The entries of a map are iterated through in an unspecified order. - PushStep(PathStep) - - // Report is called exactly once on leaf nodes to report whether the - // comparison identified the node as equal, unequal, or ignored. - // A leaf node is one that is immediately preceded by and followed by - // a pair of PushStep and PopStep calls. - Report(Result) - - // PopStep ascends back up the value tree. - // There is always a matching pop call for every push call. - PopStep() -}) Option { - return reporter{r} -} - -type reporter struct{ reporterIface } -type reporterIface interface { - PushStep(PathStep) - Report(Result) - PopStep() -} - -func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { - panic("not implemented") -} - -// normalizeOption normalizes the input options such that all Options groups -// are flattened and groups with a single element are reduced to that element. -// Only coreOptions and Options containing coreOptions are allowed. -func normalizeOption(src Option) Option { - switch opts := flattenOptions(nil, Options{src}); len(opts) { - case 0: - return nil - case 1: - return opts[0] - default: - return opts - } -} - -// flattenOptions copies all options in src to dst as a flat list. -// Only coreOptions and Options containing coreOptions are allowed. -func flattenOptions(dst, src Options) Options { - for _, opt := range src { - switch opt := opt.(type) { - case nil: - continue - case Options: - dst = flattenOptions(dst, opt) - case coreOption: - dst = append(dst, opt) - default: - panic(fmt.Sprintf("invalid option type: %T", opt)) - } - } - return dst -} diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go deleted file mode 100644 index a0a58850..00000000 --- a/vendor/github.com/google/go-cmp/cmp/path.go +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "fmt" - "reflect" - "strings" - "unicode" - "unicode/utf8" - - "github.com/google/go-cmp/cmp/internal/value" -) - -// Path is a list of PathSteps describing the sequence of operations to get -// from some root type to the current position in the value tree. -// The first Path element is always an operation-less PathStep that exists -// simply to identify the initial type. -// -// When traversing structs with embedded structs, the embedded struct will -// always be accessed as a field before traversing the fields of the -// embedded struct themselves. That is, an exported field from the -// embedded struct will never be accessed directly from the parent struct. -type Path []PathStep - -// PathStep is a union-type for specific operations to traverse -// a value's tree structure. Users of this package never need to implement -// these types as values of this type will be returned by this package. -// -// Implementations of this interface are -// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. -type PathStep interface { - String() string - - // Type is the resulting type after performing the path step. - Type() reflect.Type - - // Values is the resulting values after performing the path step. - // The type of each valid value is guaranteed to be identical to Type. - // - // In some cases, one or both may be invalid or have restrictions: - // - For StructField, both are not interface-able if the current field - // is unexported and the struct type is not explicitly permitted by - // an Exporter to traverse unexported fields. - // - For SliceIndex, one may be invalid if an element is missing from - // either the x or y slice. - // - For MapIndex, one may be invalid if an entry is missing from - // either the x or y map. - // - // The provided values must not be mutated. - Values() (vx, vy reflect.Value) -} - -var ( - _ PathStep = StructField{} - _ PathStep = SliceIndex{} - _ PathStep = MapIndex{} - _ PathStep = Indirect{} - _ PathStep = TypeAssertion{} - _ PathStep = Transform{} -) - -func (pa *Path) push(s PathStep) { - *pa = append(*pa, s) -} - -func (pa *Path) pop() { - *pa = (*pa)[:len(*pa)-1] -} - -// Last returns the last PathStep in the Path. -// If the path is empty, this returns a non-nil PathStep that reports a nil Type. -func (pa Path) Last() PathStep { - return pa.Index(-1) -} - -// Index returns the ith step in the Path and supports negative indexing. -// A negative index starts counting from the tail of the Path such that -1 -// refers to the last step, -2 refers to the second-to-last step, and so on. -// If index is invalid, this returns a non-nil PathStep that reports a nil Type. -func (pa Path) Index(i int) PathStep { - if i < 0 { - i = len(pa) + i - } - if i < 0 || i >= len(pa) { - return pathStep{} - } - return pa[i] -} - -// String returns the simplified path to a node. -// The simplified path only contains struct field accesses. -// -// For example: -// -// MyMap.MySlices.MyField -func (pa Path) String() string { - var ss []string - for _, s := range pa { - if _, ok := s.(StructField); ok { - ss = append(ss, s.String()) - } - } - return strings.TrimPrefix(strings.Join(ss, ""), ".") -} - -// GoString returns the path to a specific node using Go syntax. -// -// For example: -// -// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField -func (pa Path) GoString() string { - var ssPre, ssPost []string - var numIndirect int - for i, s := range pa { - var nextStep PathStep - if i+1 < len(pa) { - nextStep = pa[i+1] - } - switch s := s.(type) { - case Indirect: - numIndirect++ - pPre, pPost := "(", ")" - switch nextStep.(type) { - case Indirect: - continue // Next step is indirection, so let them batch up - case StructField: - numIndirect-- // Automatic indirection on struct fields - case nil: - pPre, pPost = "", "" // Last step; no need for parenthesis - } - if numIndirect > 0 { - ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) - ssPost = append(ssPost, pPost) - } - numIndirect = 0 - continue - case Transform: - ssPre = append(ssPre, s.trans.name+"(") - ssPost = append(ssPost, ")") - continue - } - ssPost = append(ssPost, s.String()) - } - for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { - ssPre[i], ssPre[j] = ssPre[j], ssPre[i] - } - return strings.Join(ssPre, "") + strings.Join(ssPost, "") -} - -type pathStep struct { - typ reflect.Type - vx, vy reflect.Value -} - -func (ps pathStep) Type() reflect.Type { return ps.typ } -func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } -func (ps pathStep) String() string { - if ps.typ == nil { - return "" - } - s := value.TypeString(ps.typ, false) - if s == "" || strings.ContainsAny(s, "{}\n") { - return "root" // Type too simple or complex to print - } - return fmt.Sprintf("{%s}", s) -} - -// StructField represents a struct field access on a field called Name. -type StructField struct{ *structField } -type structField struct { - pathStep - name string - idx int - - // These fields are used for forcibly accessing an unexported field. - // pvx, pvy, and field are only valid if unexported is true. - unexported bool - mayForce bool // Forcibly allow visibility - paddr bool // Was parent addressable? - pvx, pvy reflect.Value // Parent values (always addressable) - field reflect.StructField // Field information -} - -func (sf StructField) Type() reflect.Type { return sf.typ } -func (sf StructField) Values() (vx, vy reflect.Value) { - if !sf.unexported { - return sf.vx, sf.vy // CanInterface reports true - } - - // Forcibly obtain read-write access to an unexported struct field. - if sf.mayForce { - vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) - vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) - return vx, vy // CanInterface reports true - } - return sf.vx, sf.vy // CanInterface reports false -} -func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } - -// Name is the field name. -func (sf StructField) Name() string { return sf.name } - -// Index is the index of the field in the parent struct type. -// See reflect.Type.Field. -func (sf StructField) Index() int { return sf.idx } - -// SliceIndex is an index operation on a slice or array at some index Key. -type SliceIndex struct{ *sliceIndex } -type sliceIndex struct { - pathStep - xkey, ykey int - isSlice bool // False for reflect.Array -} - -func (si SliceIndex) Type() reflect.Type { return si.typ } -func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } -func (si SliceIndex) String() string { - switch { - case si.xkey == si.ykey: - return fmt.Sprintf("[%d]", si.xkey) - case si.ykey == -1: - // [5->?] means "I don't know where X[5] went" - return fmt.Sprintf("[%d->?]", si.xkey) - case si.xkey == -1: - // [?->3] means "I don't know where Y[3] came from" - return fmt.Sprintf("[?->%d]", si.ykey) - default: - // [5->3] means "X[5] moved to Y[3]" - return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) - } -} - -// Key is the index key; it may return -1 if in a split state -func (si SliceIndex) Key() int { - if si.xkey != si.ykey { - return -1 - } - return si.xkey -} - -// SplitKeys are the indexes for indexing into slices in the -// x and y values, respectively. These indexes may differ due to the -// insertion or removal of an element in one of the slices, causing -// all of the indexes to be shifted. If an index is -1, then that -// indicates that the element does not exist in the associated slice. -// -// Key is guaranteed to return -1 if and only if the indexes returned -// by SplitKeys are not the same. SplitKeys will never return -1 for -// both indexes. -func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } - -// MapIndex is an index operation on a map at some index Key. -type MapIndex struct{ *mapIndex } -type mapIndex struct { - pathStep - key reflect.Value -} - -func (mi MapIndex) Type() reflect.Type { return mi.typ } -func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } -func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } - -// Key is the value of the map key. -func (mi MapIndex) Key() reflect.Value { return mi.key } - -// Indirect represents pointer indirection on the parent type. -type Indirect struct{ *indirect } -type indirect struct { - pathStep -} - -func (in Indirect) Type() reflect.Type { return in.typ } -func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } -func (in Indirect) String() string { return "*" } - -// TypeAssertion represents a type assertion on an interface. -type TypeAssertion struct{ *typeAssertion } -type typeAssertion struct { - pathStep -} - -func (ta TypeAssertion) Type() reflect.Type { return ta.typ } -func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } -func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } - -// Transform is a transformation from the parent type to the current type. -type Transform struct{ *transform } -type transform struct { - pathStep - trans *transformer -} - -func (tf Transform) Type() reflect.Type { return tf.typ } -func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } -func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } - -// Name is the name of the Transformer. -func (tf Transform) Name() string { return tf.trans.name } - -// Func is the function pointer to the transformer function. -func (tf Transform) Func() reflect.Value { return tf.trans.fnc } - -// Option returns the originally constructed Transformer option. -// The == operator can be used to detect the exact option used. -func (tf Transform) Option() Option { return tf.trans } - -// pointerPath represents a dual-stack of pointers encountered when -// recursively traversing the x and y values. This data structure supports -// detection of cycles and determining whether the cycles are equal. -// In Go, cycles can occur via pointers, slices, and maps. -// -// The pointerPath uses a map to represent a stack; where descension into a -// pointer pushes the address onto the stack, and ascension from a pointer -// pops the address from the stack. Thus, when traversing into a pointer from -// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles -// by checking whether the pointer has already been visited. The cycle detection -// uses a separate stack for the x and y values. -// -// If a cycle is detected we need to determine whether the two pointers -// should be considered equal. The definition of equality chosen by Equal -// requires two graphs to have the same structure. To determine this, both the -// x and y values must have a cycle where the previous pointers were also -// encountered together as a pair. -// -// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and -// MapIndex with pointer information for the x and y values. -// Suppose px and py are two pointers to compare, we then search the -// Path for whether px was ever encountered in the Path history of x, and -// similarly so with py. If either side has a cycle, the comparison is only -// equal if both px and py have a cycle resulting from the same PathStep. -// -// Using a map as a stack is more performant as we can perform cycle detection -// in O(1) instead of O(N) where N is len(Path). -type pointerPath struct { - // mx is keyed by x pointers, where the value is the associated y pointer. - mx map[value.Pointer]value.Pointer - // my is keyed by y pointers, where the value is the associated x pointer. - my map[value.Pointer]value.Pointer -} - -func (p *pointerPath) Init() { - p.mx = make(map[value.Pointer]value.Pointer) - p.my = make(map[value.Pointer]value.Pointer) -} - -// Push indicates intent to descend into pointers vx and vy where -// visited reports whether either has been seen before. If visited before, -// equal reports whether both pointers were encountered together. -// Pop must be called if and only if the pointers were never visited. -// -// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map -// and be non-nil. -func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { - px := value.PointerOf(vx) - py := value.PointerOf(vy) - _, ok1 := p.mx[px] - _, ok2 := p.my[py] - if ok1 || ok2 { - equal = p.mx[px] == py && p.my[py] == px // Pointers paired together - return equal, true - } - p.mx[px] = py - p.my[py] = px - return false, false -} - -// Pop ascends from pointers vx and vy. -func (p pointerPath) Pop(vx, vy reflect.Value) { - delete(p.mx, value.PointerOf(vx)) - delete(p.my, value.PointerOf(vy)) -} - -// isExported reports whether the identifier is exported. -func isExported(id string) bool { - r, _ := utf8.DecodeRuneInString(id) - return unicode.IsUpper(r) -} diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go deleted file mode 100644 index f43cd12e..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -// defaultReporter implements the reporter interface. -// -// As Equal serially calls the PushStep, Report, and PopStep methods, the -// defaultReporter constructs a tree-based representation of the compared value -// and the result of each comparison (see valueNode). -// -// When the String method is called, the FormatDiff method transforms the -// valueNode tree into a textNode tree, which is a tree-based representation -// of the textual output (see textNode). -// -// Lastly, the textNode.String method produces the final report as a string. -type defaultReporter struct { - root *valueNode - curr *valueNode -} - -func (r *defaultReporter) PushStep(ps PathStep) { - r.curr = r.curr.PushStep(ps) - if r.root == nil { - r.root = r.curr - } -} -func (r *defaultReporter) Report(rs Result) { - r.curr.Report(rs) -} -func (r *defaultReporter) PopStep() { - r.curr = r.curr.PopStep() -} - -// String provides a full report of the differences detected as a structured -// literal in pseudo-Go syntax. String may only be called after the entire tree -// has been traversed. -func (r *defaultReporter) String() string { - assert(r.root != nil && r.curr == nil) - if r.root.NumDiff == 0 { - return "" - } - ptrs := new(pointerReferences) - text := formatOptions{}.FormatDiff(r.root, ptrs) - resolveReferences(text) - return text.String() -} - -func assert(ok bool) { - if !ok { - panic("assertion failure") - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go deleted file mode 100644 index 2050bf6b..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_compare.go +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "fmt" - "reflect" -) - -// numContextRecords is the number of surrounding equal records to print. -const numContextRecords = 2 - -type diffMode byte - -const ( - diffUnknown diffMode = 0 - diffIdentical diffMode = ' ' - diffRemoved diffMode = '-' - diffInserted diffMode = '+' -) - -type typeMode int - -const ( - // emitType always prints the type. - emitType typeMode = iota - // elideType never prints the type. - elideType - // autoType prints the type only for composite kinds - // (i.e., structs, slices, arrays, and maps). - autoType -) - -type formatOptions struct { - // DiffMode controls the output mode of FormatDiff. - // - // If diffUnknown, then produce a diff of the x and y values. - // If diffIdentical, then emit values as if they were equal. - // If diffRemoved, then only emit x values (ignoring y values). - // If diffInserted, then only emit y values (ignoring x values). - DiffMode diffMode - - // TypeMode controls whether to print the type for the current node. - // - // As a general rule of thumb, we always print the type of the next node - // after an interface, and always elide the type of the next node after - // a slice or map node. - TypeMode typeMode - - // formatValueOptions are options specific to printing reflect.Values. - formatValueOptions -} - -func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { - opts.DiffMode = d - return opts -} -func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { - opts.TypeMode = t - return opts -} -func (opts formatOptions) WithVerbosity(level int) formatOptions { - opts.VerbosityLevel = level - opts.LimitVerbosity = true - return opts -} -func (opts formatOptions) verbosity() uint { - switch { - case opts.VerbosityLevel < 0: - return 0 - case opts.VerbosityLevel > 16: - return 16 // some reasonable maximum to avoid shift overflow - default: - return uint(opts.VerbosityLevel) - } -} - -const maxVerbosityPreset = 6 - -// verbosityPreset modifies the verbosity settings given an index -// between 0 and maxVerbosityPreset, inclusive. -func verbosityPreset(opts formatOptions, i int) formatOptions { - opts.VerbosityLevel = int(opts.verbosity()) + 2*i - if i > 0 { - opts.AvoidStringer = true - } - if i >= maxVerbosityPreset { - opts.PrintAddresses = true - opts.QualifiedNames = true - } - return opts -} - -// FormatDiff converts a valueNode tree into a textNode tree, where the later -// is a textual representation of the differences detected in the former. -func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { - if opts.DiffMode == diffIdentical { - opts = opts.WithVerbosity(1) - } else if opts.verbosity() < 3 { - opts = opts.WithVerbosity(3) - } - - // Check whether we have specialized formatting for this node. - // This is not necessary, but helpful for producing more readable outputs. - if opts.CanFormatDiffSlice(v) { - return opts.FormatDiffSlice(v) - } - - var parentKind reflect.Kind - if v.parent != nil && v.parent.TransformerName == "" { - parentKind = v.parent.Type.Kind() - } - - // For leaf nodes, format the value based on the reflect.Values alone. - // As a special case, treat equal []byte as a leaf nodes. - isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == byteType - isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0 - if v.MaxDepth == 0 || isEqualBytes { - switch opts.DiffMode { - case diffUnknown, diffIdentical: - // Format Equal. - if v.NumDiff == 0 { - outx := opts.FormatValue(v.ValueX, parentKind, ptrs) - outy := opts.FormatValue(v.ValueY, parentKind, ptrs) - if v.NumIgnored > 0 && v.NumSame == 0 { - return textEllipsis - } else if outx.Len() < outy.Len() { - return outx - } else { - return outy - } - } - - // Format unequal. - assert(opts.DiffMode == diffUnknown) - var list textList - outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) - outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) - for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { - opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) - outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) - outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) - } - if outx != nil { - list = append(list, textRecord{Diff: '-', Value: outx}) - } - if outy != nil { - list = append(list, textRecord{Diff: '+', Value: outy}) - } - return opts.WithTypeMode(emitType).FormatType(v.Type, list) - case diffRemoved: - return opts.FormatValue(v.ValueX, parentKind, ptrs) - case diffInserted: - return opts.FormatValue(v.ValueY, parentKind, ptrs) - default: - panic("invalid diff mode") - } - } - - // Register slice element to support cycle detection. - if parentKind == reflect.Slice { - ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) - defer ptrs.Pop() - defer func() { out = wrapTrunkReferences(ptrRefs, out) }() - } - - // Descend into the child value node. - if v.TransformerName != "" { - out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) - out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} - return opts.FormatType(v.Type, out) - } else { - switch k := v.Type.Kind(); k { - case reflect.Struct, reflect.Array, reflect.Slice: - out = opts.formatDiffList(v.Records, k, ptrs) - out = opts.FormatType(v.Type, out) - case reflect.Map: - // Register map to support cycle detection. - ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) - defer ptrs.Pop() - - out = opts.formatDiffList(v.Records, k, ptrs) - out = wrapTrunkReferences(ptrRefs, out) - out = opts.FormatType(v.Type, out) - case reflect.Ptr: - // Register pointer to support cycle detection. - ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) - defer ptrs.Pop() - - out = opts.FormatDiff(v.Value, ptrs) - out = wrapTrunkReferences(ptrRefs, out) - out = &textWrap{Prefix: "&", Value: out} - case reflect.Interface: - out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) - default: - panic(fmt.Sprintf("%v cannot have children", k)) - } - return out - } -} - -func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { - // Derive record name based on the data structure kind. - var name string - var formatKey func(reflect.Value) string - switch k { - case reflect.Struct: - name = "field" - opts = opts.WithTypeMode(autoType) - formatKey = func(v reflect.Value) string { return v.String() } - case reflect.Slice, reflect.Array: - name = "element" - opts = opts.WithTypeMode(elideType) - formatKey = func(reflect.Value) string { return "" } - case reflect.Map: - name = "entry" - opts = opts.WithTypeMode(elideType) - formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } - } - - maxLen := -1 - if opts.LimitVerbosity { - if opts.DiffMode == diffIdentical { - maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... - } else { - maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... - } - opts.VerbosityLevel-- - } - - // Handle unification. - switch opts.DiffMode { - case diffIdentical, diffRemoved, diffInserted: - var list textList - var deferredEllipsis bool // Add final "..." to indicate records were dropped - for _, r := range recs { - if len(list) == maxLen { - deferredEllipsis = true - break - } - - // Elide struct fields that are zero value. - if k == reflect.Struct { - var isZero bool - switch opts.DiffMode { - case diffIdentical: - isZero = r.Value.ValueX.IsZero() || r.Value.ValueY.IsZero() - case diffRemoved: - isZero = r.Value.ValueX.IsZero() - case diffInserted: - isZero = r.Value.ValueY.IsZero() - } - if isZero { - continue - } - } - // Elide ignored nodes. - if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { - deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) - if !deferredEllipsis { - list.AppendEllipsis(diffStats{}) - } - continue - } - if out := opts.FormatDiff(r.Value, ptrs); out != nil { - list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) - } - } - if deferredEllipsis { - list.AppendEllipsis(diffStats{}) - } - return &textWrap{Prefix: "{", Value: list, Suffix: "}"} - case diffUnknown: - default: - panic("invalid diff mode") - } - - // Handle differencing. - var numDiffs int - var list textList - var keys []reflect.Value // invariant: len(list) == len(keys) - groups := coalesceAdjacentRecords(name, recs) - maxGroup := diffStats{Name: name} - for i, ds := range groups { - if maxLen >= 0 && numDiffs >= maxLen { - maxGroup = maxGroup.Append(ds) - continue - } - - // Handle equal records. - if ds.NumDiff() == 0 { - // Compute the number of leading and trailing records to print. - var numLo, numHi int - numEqual := ds.NumIgnored + ds.NumIdentical - for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { - if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { - break - } - numLo++ - } - for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { - if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { - break - } - numHi++ - } - if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { - numHi++ // Avoid pointless coalescing of a single equal record - } - - // Format the equal values. - for _, r := range recs[:numLo] { - out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) - list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) - keys = append(keys, r.Key) - } - if numEqual > numLo+numHi { - ds.NumIdentical -= numLo + numHi - list.AppendEllipsis(ds) - for len(keys) < len(list) { - keys = append(keys, reflect.Value{}) - } - } - for _, r := range recs[numEqual-numHi : numEqual] { - out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) - list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) - keys = append(keys, r.Key) - } - recs = recs[numEqual:] - continue - } - - // Handle unequal records. - for _, r := range recs[:ds.NumDiff()] { - switch { - case opts.CanFormatDiffSlice(r.Value): - out := opts.FormatDiffSlice(r.Value) - list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) - keys = append(keys, r.Key) - case r.Value.NumChildren == r.Value.MaxDepth: - outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) - outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) - for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { - opts2 := verbosityPreset(opts, i) - outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) - outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) - } - if outx != nil { - list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) - keys = append(keys, r.Key) - } - if outy != nil { - list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) - keys = append(keys, r.Key) - } - default: - out := opts.FormatDiff(r.Value, ptrs) - list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) - keys = append(keys, r.Key) - } - } - recs = recs[ds.NumDiff():] - numDiffs += ds.NumDiff() - } - if maxGroup.IsZero() { - assert(len(recs) == 0) - } else { - list.AppendEllipsis(maxGroup) - for len(keys) < len(list) { - keys = append(keys, reflect.Value{}) - } - } - assert(len(list) == len(keys)) - - // For maps, the default formatting logic uses fmt.Stringer which may - // produce ambiguous output. Avoid calling String to disambiguate. - if k == reflect.Map { - var ambiguous bool - seenKeys := map[string]reflect.Value{} - for i, currKey := range keys { - if currKey.IsValid() { - strKey := list[i].Key - prevKey, seen := seenKeys[strKey] - if seen && prevKey.CanInterface() && currKey.CanInterface() { - ambiguous = prevKey.Interface() != currKey.Interface() - if ambiguous { - break - } - } - seenKeys[strKey] = currKey - } - } - if ambiguous { - for i, k := range keys { - if k.IsValid() { - list[i].Key = formatMapKey(k, true, ptrs) - } - } - } - } - - return &textWrap{Prefix: "{", Value: list, Suffix: "}"} -} - -// coalesceAdjacentRecords coalesces the list of records into groups of -// adjacent equal, or unequal counts. -func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { - var prevCase int // Arbitrary index into which case last occurred - lastStats := func(i int) *diffStats { - if prevCase != i { - groups = append(groups, diffStats{Name: name}) - prevCase = i - } - return &groups[len(groups)-1] - } - for _, r := range recs { - switch rv := r.Value; { - case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: - lastStats(1).NumIgnored++ - case rv.NumDiff == 0: - lastStats(1).NumIdentical++ - case rv.NumDiff > 0 && !rv.ValueY.IsValid(): - lastStats(2).NumRemoved++ - case rv.NumDiff > 0 && !rv.ValueX.IsValid(): - lastStats(2).NumInserted++ - default: - lastStats(2).NumModified++ - } - } - return groups -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_references.go b/vendor/github.com/google/go-cmp/cmp/report_references.go deleted file mode 100644 index be31b33a..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_references.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2020, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "fmt" - "reflect" - "strings" - - "github.com/google/go-cmp/cmp/internal/flags" - "github.com/google/go-cmp/cmp/internal/value" -) - -const ( - pointerDelimPrefix = "⟪" - pointerDelimSuffix = "⟫" -) - -// formatPointer prints the address of the pointer. -func formatPointer(p value.Pointer, withDelims bool) string { - v := p.Uintptr() - if flags.Deterministic { - v = 0xdeadf00f // Only used for stable testing purposes - } - if withDelims { - return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix - } - return formatHex(uint64(v)) -} - -// pointerReferences is a stack of pointers visited so far. -type pointerReferences [][2]value.Pointer - -func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { - if deref && vx.IsValid() { - vx = vx.Addr() - } - if deref && vy.IsValid() { - vy = vy.Addr() - } - switch d { - case diffUnknown, diffIdentical: - pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} - case diffRemoved: - pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} - case diffInserted: - pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} - } - *ps = append(*ps, pp) - return pp -} - -func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { - p = value.PointerOf(v) - for _, pp := range *ps { - if p == pp[0] || p == pp[1] { - return p, true - } - } - *ps = append(*ps, [2]value.Pointer{p, p}) - return p, false -} - -func (ps *pointerReferences) Pop() { - *ps = (*ps)[:len(*ps)-1] -} - -// trunkReferences is metadata for a textNode indicating that the sub-tree -// represents the value for either pointer in a pair of references. -type trunkReferences struct{ pp [2]value.Pointer } - -// trunkReference is metadata for a textNode indicating that the sub-tree -// represents the value for the given pointer reference. -type trunkReference struct{ p value.Pointer } - -// leafReference is metadata for a textNode indicating that the value is -// truncated as it refers to another part of the tree (i.e., a trunk). -type leafReference struct{ p value.Pointer } - -func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { - switch { - case pp[0].IsNil(): - return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} - case pp[1].IsNil(): - return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} - case pp[0] == pp[1]: - return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} - default: - return &textWrap{Value: s, Metadata: trunkReferences{pp}} - } -} -func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { - var prefix string - if printAddress { - prefix = formatPointer(p, true) - } - return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} -} -func makeLeafReference(p value.Pointer, printAddress bool) textNode { - out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} - var prefix string - if printAddress { - prefix = formatPointer(p, true) - } - return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} -} - -// resolveReferences walks the textNode tree searching for any leaf reference -// metadata and resolves each against the corresponding trunk references. -// Since pointer addresses in memory are not particularly readable to the user, -// it replaces each pointer value with an arbitrary and unique reference ID. -func resolveReferences(s textNode) { - var walkNodes func(textNode, func(textNode)) - walkNodes = func(s textNode, f func(textNode)) { - f(s) - switch s := s.(type) { - case *textWrap: - walkNodes(s.Value, f) - case textList: - for _, r := range s { - walkNodes(r.Value, f) - } - } - } - - // Collect all trunks and leaves with reference metadata. - var trunks, leaves []*textWrap - walkNodes(s, func(s textNode) { - if s, ok := s.(*textWrap); ok { - switch s.Metadata.(type) { - case leafReference: - leaves = append(leaves, s) - case trunkReference, trunkReferences: - trunks = append(trunks, s) - } - } - }) - - // No leaf references to resolve. - if len(leaves) == 0 { - return - } - - // Collect the set of all leaf references to resolve. - leafPtrs := make(map[value.Pointer]bool) - for _, leaf := range leaves { - leafPtrs[leaf.Metadata.(leafReference).p] = true - } - - // Collect the set of trunk pointers that are always paired together. - // This allows us to assign a single ID to both pointers for brevity. - // If a pointer in a pair ever occurs by itself or as a different pair, - // then the pair is broken. - pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) - unpair := func(p value.Pointer) { - if !pairedTrunkPtrs[p].IsNil() { - pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half - } - pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half - } - for _, trunk := range trunks { - switch p := trunk.Metadata.(type) { - case trunkReference: - unpair(p.p) // standalone pointer cannot be part of a pair - case trunkReferences: - p0, ok0 := pairedTrunkPtrs[p.pp[0]] - p1, ok1 := pairedTrunkPtrs[p.pp[1]] - switch { - case !ok0 && !ok1: - // Register the newly seen pair. - pairedTrunkPtrs[p.pp[0]] = p.pp[1] - pairedTrunkPtrs[p.pp[1]] = p.pp[0] - case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: - // Exact pair already seen; do nothing. - default: - // Pair conflicts with some other pair; break all pairs. - unpair(p.pp[0]) - unpair(p.pp[1]) - } - } - } - - // Correlate each pointer referenced by leaves to a unique identifier, - // and print the IDs for each trunk that matches those pointers. - var nextID uint - ptrIDs := make(map[value.Pointer]uint) - newID := func() uint { - id := nextID - nextID++ - return id - } - for _, trunk := range trunks { - switch p := trunk.Metadata.(type) { - case trunkReference: - if print := leafPtrs[p.p]; print { - id, ok := ptrIDs[p.p] - if !ok { - id = newID() - ptrIDs[p.p] = id - } - trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) - } - case trunkReferences: - print0 := leafPtrs[p.pp[0]] - print1 := leafPtrs[p.pp[1]] - if print0 || print1 { - id0, ok0 := ptrIDs[p.pp[0]] - id1, ok1 := ptrIDs[p.pp[1]] - isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] - if isPair { - var id uint - assert(ok0 == ok1) // must be seen together or not at all - if ok0 { - assert(id0 == id1) // must have the same ID - id = id0 - } else { - id = newID() - ptrIDs[p.pp[0]] = id - ptrIDs[p.pp[1]] = id - } - trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) - } else { - if print0 && !ok0 { - id0 = newID() - ptrIDs[p.pp[0]] = id0 - } - if print1 && !ok1 { - id1 = newID() - ptrIDs[p.pp[1]] = id1 - } - switch { - case print0 && print1: - trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) - case print0: - trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) - case print1: - trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) - } - } - } - } - } - - // Update all leaf references with the unique identifier. - for _, leaf := range leaves { - if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { - leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) - } - } -} - -func formatReference(id uint) string { - return fmt.Sprintf("ref#%d", id) -} - -func updateReferencePrefix(prefix, ref string) string { - if prefix == "" { - return pointerDelimPrefix + ref + pointerDelimSuffix - } - suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) - return pointerDelimPrefix + ref + ": " + suffix -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go deleted file mode 100644 index 2ab41fad..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "bytes" - "fmt" - "reflect" - "strconv" - "strings" - "unicode" - "unicode/utf8" - - "github.com/google/go-cmp/cmp/internal/value" -) - -var ( - anyType = reflect.TypeOf((*interface{})(nil)).Elem() - stringType = reflect.TypeOf((*string)(nil)).Elem() - bytesType = reflect.TypeOf((*[]byte)(nil)).Elem() - byteType = reflect.TypeOf((*byte)(nil)).Elem() -) - -type formatValueOptions struct { - // AvoidStringer controls whether to avoid calling custom stringer - // methods like error.Error or fmt.Stringer.String. - AvoidStringer bool - - // PrintAddresses controls whether to print the address of all pointers, - // slice elements, and maps. - PrintAddresses bool - - // QualifiedNames controls whether FormatType uses the fully qualified name - // (including the full package path as opposed to just the package name). - QualifiedNames bool - - // VerbosityLevel controls the amount of output to produce. - // A higher value produces more output. A value of zero or lower produces - // no output (represented using an ellipsis). - // If LimitVerbosity is false, then the level is treated as infinite. - VerbosityLevel int - - // LimitVerbosity specifies that formatting should respect VerbosityLevel. - LimitVerbosity bool -} - -// FormatType prints the type as if it were wrapping s. -// This may return s as-is depending on the current type and TypeMode mode. -func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { - // Check whether to emit the type or not. - switch opts.TypeMode { - case autoType: - switch t.Kind() { - case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: - if s.Equal(textNil) { - return s - } - default: - return s - } - if opts.DiffMode == diffIdentical { - return s // elide type for identical nodes - } - case elideType: - return s - } - - // Determine the type label, applying special handling for unnamed types. - typeName := value.TypeString(t, opts.QualifiedNames) - if t.Name() == "" { - // According to Go grammar, certain type literals contain symbols that - // do not strongly bind to the next lexicographical token (e.g., *T). - switch t.Kind() { - case reflect.Chan, reflect.Func, reflect.Ptr: - typeName = "(" + typeName + ")" - } - } - return &textWrap{Prefix: typeName, Value: wrapParens(s)} -} - -// wrapParens wraps s with a set of parenthesis, but avoids it if the -// wrapped node itself is already surrounded by a pair of parenthesis or braces. -// It handles unwrapping one level of pointer-reference nodes. -func wrapParens(s textNode) textNode { - var refNode *textWrap - if s2, ok := s.(*textWrap); ok { - // Unwrap a single pointer reference node. - switch s2.Metadata.(type) { - case leafReference, trunkReference, trunkReferences: - refNode = s2 - if s3, ok := refNode.Value.(*textWrap); ok { - s2 = s3 - } - } - - // Already has delimiters that make parenthesis unnecessary. - hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") - hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") - if hasParens || hasBraces { - return s - } - } - if refNode != nil { - refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} - return s - } - return &textWrap{Prefix: "(", Value: s, Suffix: ")"} -} - -// FormatValue prints the reflect.Value, taking extra care to avoid descending -// into pointers already in ptrs. As pointers are visited, ptrs is also updated. -func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { - if !v.IsValid() { - return nil - } - t := v.Type() - - // Check slice element for cycles. - if parentKind == reflect.Slice { - ptrRef, visited := ptrs.Push(v.Addr()) - if visited { - return makeLeafReference(ptrRef, false) - } - defer ptrs.Pop() - defer func() { out = wrapTrunkReference(ptrRef, false, out) }() - } - - // Check whether there is an Error or String method to call. - if !opts.AvoidStringer && v.CanInterface() { - // Avoid calling Error or String methods on nil receivers since many - // implementations crash when doing so. - if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { - var prefix, strVal string - func() { - // Swallow and ignore any panics from String or Error. - defer func() { recover() }() - switch v := v.Interface().(type) { - case error: - strVal = v.Error() - prefix = "e" - case fmt.Stringer: - strVal = v.String() - prefix = "s" - } - }() - if prefix != "" { - return opts.formatString(prefix, strVal) - } - } - } - - // Check whether to explicitly wrap the result with the type. - var skipType bool - defer func() { - if !skipType { - out = opts.FormatType(t, out) - } - }() - - switch t.Kind() { - case reflect.Bool: - return textLine(fmt.Sprint(v.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return textLine(fmt.Sprint(v.Int())) - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return textLine(fmt.Sprint(v.Uint())) - case reflect.Uint8: - if parentKind == reflect.Slice || parentKind == reflect.Array { - return textLine(formatHex(v.Uint())) - } - return textLine(fmt.Sprint(v.Uint())) - case reflect.Uintptr: - return textLine(formatHex(v.Uint())) - case reflect.Float32, reflect.Float64: - return textLine(fmt.Sprint(v.Float())) - case reflect.Complex64, reflect.Complex128: - return textLine(fmt.Sprint(v.Complex())) - case reflect.String: - return opts.formatString("", v.String()) - case reflect.UnsafePointer, reflect.Chan, reflect.Func: - return textLine(formatPointer(value.PointerOf(v), true)) - case reflect.Struct: - var list textList - v := makeAddressable(v) // needed for retrieveUnexportedField - maxLen := v.NumField() - if opts.LimitVerbosity { - maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... - opts.VerbosityLevel-- - } - for i := 0; i < v.NumField(); i++ { - vv := v.Field(i) - if vv.IsZero() { - continue // Elide fields with zero values - } - if len(list) == maxLen { - list.AppendEllipsis(diffStats{}) - break - } - sf := t.Field(i) - if supportExporters && !isExported(sf.Name) { - vv = retrieveUnexportedField(v, sf, true) - } - s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) - list = append(list, textRecord{Key: sf.Name, Value: s}) - } - return &textWrap{Prefix: "{", Value: list, Suffix: "}"} - case reflect.Slice: - if v.IsNil() { - return textNil - } - - // Check whether this is a []byte of text data. - if t.Elem() == byteType { - b := v.Bytes() - isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) } - if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { - out = opts.formatString("", string(b)) - skipType = true - return opts.FormatType(t, out) - } - } - - fallthrough - case reflect.Array: - maxLen := v.Len() - if opts.LimitVerbosity { - maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... - opts.VerbosityLevel-- - } - var list textList - for i := 0; i < v.Len(); i++ { - if len(list) == maxLen { - list.AppendEllipsis(diffStats{}) - break - } - s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) - list = append(list, textRecord{Value: s}) - } - - out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} - if t.Kind() == reflect.Slice && opts.PrintAddresses { - header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) - out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} - } - return out - case reflect.Map: - if v.IsNil() { - return textNil - } - - // Check pointer for cycles. - ptrRef, visited := ptrs.Push(v) - if visited { - return makeLeafReference(ptrRef, opts.PrintAddresses) - } - defer ptrs.Pop() - - maxLen := v.Len() - if opts.LimitVerbosity { - maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... - opts.VerbosityLevel-- - } - var list textList - for _, k := range value.SortKeys(v.MapKeys()) { - if len(list) == maxLen { - list.AppendEllipsis(diffStats{}) - break - } - sk := formatMapKey(k, false, ptrs) - sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) - list = append(list, textRecord{Key: sk, Value: sv}) - } - - out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} - out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) - return out - case reflect.Ptr: - if v.IsNil() { - return textNil - } - - // Check pointer for cycles. - ptrRef, visited := ptrs.Push(v) - if visited { - out = makeLeafReference(ptrRef, opts.PrintAddresses) - return &textWrap{Prefix: "&", Value: out} - } - defer ptrs.Pop() - - // Skip the name only if this is an unnamed pointer type. - // Otherwise taking the address of a value does not reproduce - // the named pointer type. - if v.Type().Name() == "" { - skipType = true // Let the underlying value print the type instead - } - out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) - out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) - out = &textWrap{Prefix: "&", Value: out} - return out - case reflect.Interface: - if v.IsNil() { - return textNil - } - // Interfaces accept different concrete types, - // so configure the underlying value to explicitly print the type. - return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) - default: - panic(fmt.Sprintf("%v kind not handled", v.Kind())) - } -} - -func (opts formatOptions) formatString(prefix, s string) textNode { - maxLen := len(s) - maxLines := strings.Count(s, "\n") + 1 - if opts.LimitVerbosity { - maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... - maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... - } - - // For multiline strings, use the triple-quote syntax, - // but only use it when printing removed or inserted nodes since - // we only want the extra verbosity for those cases. - lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n") - isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+') - for i := 0; i < len(lines) && isTripleQuoted; i++ { - lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support - isPrintable := func(r rune) bool { - return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable - } - line := lines[i] - isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen - } - if isTripleQuoted { - var list textList - list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) - for i, line := range lines { - if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 { - comment := commentString(fmt.Sprintf("%d elided lines", numElided)) - list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment}) - break - } - list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true}) - } - list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) - return &textWrap{Prefix: "(", Value: list, Suffix: ")"} - } - - // Format the string as a single-line quoted string. - if len(s) > maxLen+len(textEllipsis) { - return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis)) - } - return textLine(prefix + formatString(s)) -} - -// formatMapKey formats v as if it were a map key. -// The result is guaranteed to be a single line. -func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { - var opts formatOptions - opts.DiffMode = diffIdentical - opts.TypeMode = elideType - opts.PrintAddresses = disambiguate - opts.AvoidStringer = disambiguate - opts.QualifiedNames = disambiguate - opts.VerbosityLevel = maxVerbosityPreset - opts.LimitVerbosity = true - s := opts.FormatValue(v, reflect.Map, ptrs).String() - return strings.TrimSpace(s) -} - -// formatString prints s as a double-quoted or backtick-quoted string. -func formatString(s string) string { - // Use quoted string if it the same length as a raw string literal. - // Otherwise, attempt to use the raw string form. - qs := strconv.Quote(s) - if len(qs) == 1+len(s)+1 { - return qs - } - - // Disallow newlines to ensure output is a single line. - // Only allow printable runes for readability purposes. - rawInvalid := func(r rune) bool { - return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') - } - if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { - return "`" + s + "`" - } - return qs -} - -// formatHex prints u as a hexadecimal integer in Go notation. -func formatHex(u uint64) string { - var f string - switch { - case u <= 0xff: - f = "0x%02x" - case u <= 0xffff: - f = "0x%04x" - case u <= 0xffffff: - f = "0x%06x" - case u <= 0xffffffff: - f = "0x%08x" - case u <= 0xffffffffff: - f = "0x%010x" - case u <= 0xffffffffffff: - f = "0x%012x" - case u <= 0xffffffffffffff: - f = "0x%014x" - case u <= 0xffffffffffffffff: - f = "0x%016x" - } - return fmt.Sprintf(f, u) -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go deleted file mode 100644 index 23e444f6..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_slices.go +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "bytes" - "fmt" - "math" - "reflect" - "strconv" - "strings" - "unicode" - "unicode/utf8" - - "github.com/google/go-cmp/cmp/internal/diff" -) - -// CanFormatDiffSlice reports whether we support custom formatting for nodes -// that are slices of primitive kinds or strings. -func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { - switch { - case opts.DiffMode != diffUnknown: - return false // Must be formatting in diff mode - case v.NumDiff == 0: - return false // No differences detected - case !v.ValueX.IsValid() || !v.ValueY.IsValid(): - return false // Both values must be valid - case v.NumIgnored > 0: - return false // Some ignore option was used - case v.NumTransformed > 0: - return false // Some transform option was used - case v.NumCompared > 1: - return false // More than one comparison was used - case v.NumCompared == 1 && v.Type.Name() != "": - // The need for cmp to check applicability of options on every element - // in a slice is a significant performance detriment for large []byte. - // The workaround is to specify Comparer(bytes.Equal), - // which enables cmp to compare []byte more efficiently. - // If they differ, we still want to provide batched diffing. - // The logic disallows named types since they tend to have their own - // String method, with nicer formatting than what this provides. - return false - } - - // Check whether this is an interface with the same concrete types. - t := v.Type - vx, vy := v.ValueX, v.ValueY - if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() { - vx, vy = vx.Elem(), vy.Elem() - t = vx.Type() - } - - // Check whether we provide specialized diffing for this type. - switch t.Kind() { - case reflect.String: - case reflect.Array, reflect.Slice: - // Only slices of primitive types have specialized handling. - switch t.Elem().Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: - default: - return false - } - - // Both slice values have to be non-empty. - if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) { - return false - } - - // If a sufficient number of elements already differ, - // use specialized formatting even if length requirement is not met. - if v.NumDiff > v.NumSame { - return true - } - default: - return false - } - - // Use specialized string diffing for longer slices or strings. - const minLength = 32 - return vx.Len() >= minLength && vy.Len() >= minLength -} - -// FormatDiffSlice prints a diff for the slices (or strings) represented by v. -// This provides custom-tailored logic to make printing of differences in -// textual strings and slices of primitive kinds more readable. -func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { - assert(opts.DiffMode == diffUnknown) - t, vx, vy := v.Type, v.ValueX, v.ValueY - if t.Kind() == reflect.Interface { - vx, vy = vx.Elem(), vy.Elem() - t = vx.Type() - opts = opts.WithTypeMode(emitType) - } - - // Auto-detect the type of the data. - var sx, sy string - var ssx, ssy []string - var isString, isMostlyText, isPureLinedText, isBinary bool - switch { - case t.Kind() == reflect.String: - sx, sy = vx.String(), vy.String() - isString = true - case t.Kind() == reflect.Slice && t.Elem() == byteType: - sx, sy = string(vx.Bytes()), string(vy.Bytes()) - isString = true - case t.Kind() == reflect.Array: - // Arrays need to be addressable for slice operations to work. - vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() - vx2.Set(vx) - vy2.Set(vy) - vx, vy = vx2, vy2 - } - if isString { - var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int - for i, r := range sx + sy { - numTotalRunes++ - if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError { - numValidRunes++ - } - if r == '\n' { - if maxLineLen < i-lastLineIdx { - maxLineLen = i - lastLineIdx - } - lastLineIdx = i + 1 - numLines++ - } - } - isPureText := numValidRunes == numTotalRunes - isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes)) - isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024 - isBinary = !isMostlyText - - // Avoid diffing by lines if it produces a significantly more complex - // edit script than diffing by bytes. - if isPureLinedText { - ssx = strings.Split(sx, "\n") - ssy = strings.Split(sy, "\n") - esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result { - return diff.BoolResult(ssx[ix] == ssy[iy]) - }) - esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result { - return diff.BoolResult(sx[ix] == sy[iy]) - }) - efficiencyLines := float64(esLines.Dist()) / float64(len(esLines)) - efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes)) - quotedLength := len(strconv.Quote(sx + sy)) - unquotedLength := len(sx) + len(sy) - escapeExpansionRatio := float64(quotedLength) / float64(unquotedLength) - isPureLinedText = efficiencyLines < 4*efficiencyBytes || escapeExpansionRatio > 1.1 - } - } - - // Format the string into printable records. - var list textList - var delim string - switch { - // If the text appears to be multi-lined text, - // then perform differencing across individual lines. - case isPureLinedText: - list = opts.formatDiffSlice( - reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", - func(v reflect.Value, d diffMode) textRecord { - s := formatString(v.Index(0).String()) - return textRecord{Diff: d, Value: textLine(s)} - }, - ) - delim = "\n" - - // If possible, use a custom triple-quote (""") syntax for printing - // differences in a string literal. This format is more readable, - // but has edge-cases where differences are visually indistinguishable. - // This format is avoided under the following conditions: - // - A line starts with `"""` - // - A line starts with "..." - // - A line contains non-printable characters - // - Adjacent different lines differ only by whitespace - // - // For example: - // - // """ - // ... // 3 identical lines - // foo - // bar - // - baz - // + BAZ - // """ - isTripleQuoted := true - prevRemoveLines := map[string]bool{} - prevInsertLines := map[string]bool{} - var list2 textList - list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) - for _, r := range list { - if !r.Value.Equal(textEllipsis) { - line, _ := strconv.Unquote(string(r.Value.(textLine))) - line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support - normLine := strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return -1 // drop whitespace to avoid visually indistinguishable output - } - return r - }, line) - isPrintable := func(r rune) bool { - return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable - } - isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" - switch r.Diff { - case diffRemoved: - isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] - prevRemoveLines[normLine] = true - case diffInserted: - isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] - prevInsertLines[normLine] = true - } - if !isTripleQuoted { - break - } - r.Value = textLine(line) - r.ElideComma = true - } - if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group - prevRemoveLines = map[string]bool{} - prevInsertLines = map[string]bool{} - } - list2 = append(list2, r) - } - if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { - list2 = list2[:len(list2)-1] // elide single empty line at the end - } - list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) - if isTripleQuoted { - var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} - switch t.Kind() { - case reflect.String: - if t != stringType { - out = opts.FormatType(t, out) - } - case reflect.Slice: - // Always emit type for slices since the triple-quote syntax - // looks like a string (not a slice). - opts = opts.WithTypeMode(emitType) - out = opts.FormatType(t, out) - } - return out - } - - // If the text appears to be single-lined text, - // then perform differencing in approximately fixed-sized chunks. - // The output is printed as quoted strings. - case isMostlyText: - list = opts.formatDiffSlice( - reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", - func(v reflect.Value, d diffMode) textRecord { - s := formatString(v.String()) - return textRecord{Diff: d, Value: textLine(s)} - }, - ) - - // If the text appears to be binary data, - // then perform differencing in approximately fixed-sized chunks. - // The output is inspired by hexdump. - case isBinary: - list = opts.formatDiffSlice( - reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", - func(v reflect.Value, d diffMode) textRecord { - var ss []string - for i := 0; i < v.Len(); i++ { - ss = append(ss, formatHex(v.Index(i).Uint())) - } - s := strings.Join(ss, ", ") - comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) - return textRecord{Diff: d, Value: textLine(s), Comment: comment} - }, - ) - - // For all other slices of primitive types, - // then perform differencing in approximately fixed-sized chunks. - // The size of each chunk depends on the width of the element kind. - default: - var chunkSize int - if t.Elem().Kind() == reflect.Bool { - chunkSize = 16 - } else { - switch t.Elem().Bits() { - case 8: - chunkSize = 16 - case 16: - chunkSize = 12 - case 32: - chunkSize = 8 - default: - chunkSize = 8 - } - } - list = opts.formatDiffSlice( - vx, vy, chunkSize, t.Elem().Kind().String(), - func(v reflect.Value, d diffMode) textRecord { - var ss []string - for i := 0; i < v.Len(); i++ { - switch t.Elem().Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - ss = append(ss, fmt.Sprint(v.Index(i).Int())) - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - ss = append(ss, fmt.Sprint(v.Index(i).Uint())) - case reflect.Uint8, reflect.Uintptr: - ss = append(ss, formatHex(v.Index(i).Uint())) - case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: - ss = append(ss, fmt.Sprint(v.Index(i).Interface())) - } - } - s := strings.Join(ss, ", ") - return textRecord{Diff: d, Value: textLine(s)} - }, - ) - } - - // Wrap the output with appropriate type information. - var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} - if !isMostlyText { - // The "{...}" byte-sequence literal is not valid Go syntax for strings. - // Emit the type for extra clarity (e.g. "string{...}"). - if t.Kind() == reflect.String { - opts = opts.WithTypeMode(emitType) - } - return opts.FormatType(t, out) - } - switch t.Kind() { - case reflect.String: - out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} - if t != stringType { - out = opts.FormatType(t, out) - } - case reflect.Slice: - out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} - if t != bytesType { - out = opts.FormatType(t, out) - } - } - return out -} - -// formatASCII formats s as an ASCII string. -// This is useful for printing binary strings in a semi-legible way. -func formatASCII(s string) string { - b := bytes.Repeat([]byte{'.'}, len(s)) - for i := 0; i < len(s); i++ { - if ' ' <= s[i] && s[i] <= '~' { - b[i] = s[i] - } - } - return string(b) -} - -func (opts formatOptions) formatDiffSlice( - vx, vy reflect.Value, chunkSize int, name string, - makeRec func(reflect.Value, diffMode) textRecord, -) (list textList) { - eq := func(ix, iy int) bool { - return vx.Index(ix).Interface() == vy.Index(iy).Interface() - } - es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { - return diff.BoolResult(eq(ix, iy)) - }) - - appendChunks := func(v reflect.Value, d diffMode) int { - n0 := v.Len() - for v.Len() > 0 { - n := chunkSize - if n > v.Len() { - n = v.Len() - } - list = append(list, makeRec(v.Slice(0, n), d)) - v = v.Slice(n, v.Len()) - } - return n0 - v.Len() - } - - var numDiffs int - maxLen := -1 - if opts.LimitVerbosity { - maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... - opts.VerbosityLevel-- - } - - groups := coalesceAdjacentEdits(name, es) - groups = coalesceInterveningIdentical(groups, chunkSize/4) - groups = cleanupSurroundingIdentical(groups, eq) - maxGroup := diffStats{Name: name} - for i, ds := range groups { - if maxLen >= 0 && numDiffs >= maxLen { - maxGroup = maxGroup.Append(ds) - continue - } - - // Print equal. - if ds.NumDiff() == 0 { - // Compute the number of leading and trailing equal bytes to print. - var numLo, numHi int - numEqual := ds.NumIgnored + ds.NumIdentical - for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { - numLo++ - } - for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { - numHi++ - } - if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { - numHi = numEqual - numLo // Avoid pointless coalescing of single equal row - } - - // Print the equal bytes. - appendChunks(vx.Slice(0, numLo), diffIdentical) - if numEqual > numLo+numHi { - ds.NumIdentical -= numLo + numHi - list.AppendEllipsis(ds) - } - appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) - vx = vx.Slice(numEqual, vx.Len()) - vy = vy.Slice(numEqual, vy.Len()) - continue - } - - // Print unequal. - len0 := len(list) - nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) - vx = vx.Slice(nx, vx.Len()) - ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) - vy = vy.Slice(ny, vy.Len()) - numDiffs += len(list) - len0 - } - if maxGroup.IsZero() { - assert(vx.Len() == 0 && vy.Len() == 0) - } else { - list.AppendEllipsis(maxGroup) - } - return list -} - -// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent -// equal or unequal counts. -// -// Example: -// -// Input: "..XXY...Y" -// Output: [ -// {NumIdentical: 2}, -// {NumRemoved: 2, NumInserted 1}, -// {NumIdentical: 3}, -// {NumInserted: 1}, -// ] -func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { - var prevMode byte - lastStats := func(mode byte) *diffStats { - if prevMode != mode { - groups = append(groups, diffStats{Name: name}) - prevMode = mode - } - return &groups[len(groups)-1] - } - for _, e := range es { - switch e { - case diff.Identity: - lastStats('=').NumIdentical++ - case diff.UniqueX: - lastStats('!').NumRemoved++ - case diff.UniqueY: - lastStats('!').NumInserted++ - case diff.Modified: - lastStats('!').NumModified++ - } - } - return groups -} - -// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) -// equal groups into adjacent unequal groups that currently result in a -// dual inserted/removed printout. This acts as a high-pass filter to smooth -// out high-frequency changes within the windowSize. -// -// Example: -// -// WindowSize: 16, -// Input: [ -// {NumIdentical: 61}, // group 0 -// {NumRemoved: 3, NumInserted: 1}, // group 1 -// {NumIdentical: 6}, // ├── coalesce -// {NumInserted: 2}, // ├── coalesce -// {NumIdentical: 1}, // ├── coalesce -// {NumRemoved: 9}, // └── coalesce -// {NumIdentical: 64}, // group 2 -// {NumRemoved: 3, NumInserted: 1}, // group 3 -// {NumIdentical: 6}, // ├── coalesce -// {NumInserted: 2}, // ├── coalesce -// {NumIdentical: 1}, // ├── coalesce -// {NumRemoved: 7}, // ├── coalesce -// {NumIdentical: 1}, // ├── coalesce -// {NumRemoved: 2}, // └── coalesce -// {NumIdentical: 63}, // group 4 -// ] -// Output: [ -// {NumIdentical: 61}, -// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, -// {NumIdentical: 64}, -// {NumIdentical: 8, NumRemoved: 12, NumInserted: 3}, -// {NumIdentical: 63}, -// ] -func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { - groups, groupsOrig := groups[:0], groups - for i, ds := range groupsOrig { - if len(groups) >= 2 && ds.NumDiff() > 0 { - prev := &groups[len(groups)-2] // Unequal group - curr := &groups[len(groups)-1] // Equal group - next := &groupsOrig[i] // Unequal group - hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 - hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 - if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { - *prev = prev.Append(*curr).Append(*next) - groups = groups[:len(groups)-1] // Truncate off equal group - continue - } - } - groups = append(groups, ds) - } - return groups -} - -// cleanupSurroundingIdentical scans through all unequal groups, and -// moves any leading sequence of equal elements to the preceding equal group and -// moves and trailing sequence of equal elements to the succeeding equal group. -// -// This is necessary since coalesceInterveningIdentical may coalesce edit groups -// together such that leading/trailing spans of equal elements becomes possible. -// Note that this can occur even with an optimal diffing algorithm. -// -// Example: -// -// Input: [ -// {NumIdentical: 61}, -// {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements -// {NumIdentical: 67}, -// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements -// {NumIdentical: 54}, -// ] -// Output: [ -// {NumIdentical: 64}, // incremented by 3 -// {NumRemoved: 9}, -// {NumIdentical: 67}, -// {NumRemoved: 9}, -// {NumIdentical: 64}, // incremented by 10 -// ] -func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats { - var ix, iy int // indexes into sequence x and y - for i, ds := range groups { - // Handle equal group. - if ds.NumDiff() == 0 { - ix += ds.NumIdentical - iy += ds.NumIdentical - continue - } - - // Handle unequal group. - nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified - ny := ds.NumIdentical + ds.NumInserted + ds.NumModified - var numLeadingIdentical, numTrailingIdentical int - for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ { - numLeadingIdentical++ - } - for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ { - numTrailingIdentical++ - } - if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 { - if numLeadingIdentical > 0 { - // Remove leading identical span from this group and - // insert it into the preceding group. - if i-1 >= 0 { - groups[i-1].NumIdentical += numLeadingIdentical - } else { - // No preceding group exists, so prepend a new group, - // but do so after we finish iterating over all groups. - defer func() { - groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...) - }() - } - // Increment indexes since the preceding group would have handled this. - ix += numLeadingIdentical - iy += numLeadingIdentical - } - if numTrailingIdentical > 0 { - // Remove trailing identical span from this group and - // insert it into the succeeding group. - if i+1 < len(groups) { - groups[i+1].NumIdentical += numTrailingIdentical - } else { - // No succeeding group exists, so append a new group, - // but do so after we finish iterating over all groups. - defer func() { - groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical}) - }() - } - // Do not increment indexes since the succeeding group will handle this. - } - - // Update this group since some identical elements were removed. - nx -= numIdentical - ny -= numIdentical - groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny} - } - ix += nx - iy += ny - } - return groups -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go deleted file mode 100644 index 388fcf57..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_text.go +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import ( - "bytes" - "fmt" - "math/rand" - "strings" - "time" - "unicode/utf8" - - "github.com/google/go-cmp/cmp/internal/flags" -) - -var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 - -const maxColumnLength = 80 - -type indentMode int - -func (n indentMode) appendIndent(b []byte, d diffMode) []byte { - // The output of Diff is documented as being unstable to provide future - // flexibility in changing the output for more humanly readable reports. - // This logic intentionally introduces instability to the exact output - // so that users can detect accidental reliance on stability early on, - // rather than much later when an actual change to the format occurs. - if flags.Deterministic || randBool { - // Use regular spaces (U+0020). - switch d { - case diffUnknown, diffIdentical: - b = append(b, " "...) - case diffRemoved: - b = append(b, "- "...) - case diffInserted: - b = append(b, "+ "...) - } - } else { - // Use non-breaking spaces (U+00a0). - switch d { - case diffUnknown, diffIdentical: - b = append(b, "  "...) - case diffRemoved: - b = append(b, "- "...) - case diffInserted: - b = append(b, "+ "...) - } - } - return repeatCount(n).appendChar(b, '\t') -} - -type repeatCount int - -func (n repeatCount) appendChar(b []byte, c byte) []byte { - for ; n > 0; n-- { - b = append(b, c) - } - return b -} - -// textNode is a simplified tree-based representation of structured text. -// Possible node types are textWrap, textList, or textLine. -type textNode interface { - // Len reports the length in bytes of a single-line version of the tree. - // Nested textRecord.Diff and textRecord.Comment fields are ignored. - Len() int - // Equal reports whether the two trees are structurally identical. - // Nested textRecord.Diff and textRecord.Comment fields are compared. - Equal(textNode) bool - // String returns the string representation of the text tree. - // It is not guaranteed that len(x.String()) == x.Len(), - // nor that x.String() == y.String() implies that x.Equal(y). - String() string - - // formatCompactTo formats the contents of the tree as a single-line string - // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment - // fields are ignored. - // - // However, not all nodes in the tree should be collapsed as a single-line. - // If a node can be collapsed as a single-line, it is replaced by a textLine - // node. Since the top-level node cannot replace itself, this also returns - // the current node itself. - // - // This does not mutate the receiver. - formatCompactTo([]byte, diffMode) ([]byte, textNode) - // formatExpandedTo formats the contents of the tree as a multi-line string - // to the provided buffer. In order for column alignment to operate well, - // formatCompactTo must be called before calling formatExpandedTo. - formatExpandedTo([]byte, diffMode, indentMode) []byte -} - -// textWrap is a wrapper that concatenates a prefix and/or a suffix -// to the underlying node. -type textWrap struct { - Prefix string // e.g., "bytes.Buffer{" - Value textNode // textWrap | textList | textLine - Suffix string // e.g., "}" - Metadata interface{} // arbitrary metadata; has no effect on formatting -} - -func (s *textWrap) Len() int { - return len(s.Prefix) + s.Value.Len() + len(s.Suffix) -} -func (s1 *textWrap) Equal(s2 textNode) bool { - if s2, ok := s2.(*textWrap); ok { - return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix - } - return false -} -func (s *textWrap) String() string { - var d diffMode - var n indentMode - _, s2 := s.formatCompactTo(nil, d) - b := n.appendIndent(nil, d) // Leading indent - b = s2.formatExpandedTo(b, d, n) // Main body - b = append(b, '\n') // Trailing newline - return string(b) -} -func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { - n0 := len(b) // Original buffer length - b = append(b, s.Prefix...) - b, s.Value = s.Value.formatCompactTo(b, d) - b = append(b, s.Suffix...) - if _, ok := s.Value.(textLine); ok { - return b, textLine(b[n0:]) - } - return b, s -} -func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { - b = append(b, s.Prefix...) - b = s.Value.formatExpandedTo(b, d, n) - b = append(b, s.Suffix...) - return b -} - -// textList is a comma-separated list of textWrap or textLine nodes. -// The list may be formatted as multi-lines or single-line at the discretion -// of the textList.formatCompactTo method. -type textList []textRecord -type textRecord struct { - Diff diffMode // e.g., 0 or '-' or '+' - Key string // e.g., "MyField" - Value textNode // textWrap | textLine - ElideComma bool // avoid trailing comma - Comment fmt.Stringer // e.g., "6 identical fields" -} - -// AppendEllipsis appends a new ellipsis node to the list if none already -// exists at the end. If cs is non-zero it coalesces the statistics with the -// previous diffStats. -func (s *textList) AppendEllipsis(ds diffStats) { - hasStats := !ds.IsZero() - if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { - if hasStats { - *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) - } else { - *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) - } - return - } - if hasStats { - (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) - } -} - -func (s textList) Len() (n int) { - for i, r := range s { - n += len(r.Key) - if r.Key != "" { - n += len(": ") - } - n += r.Value.Len() - if i < len(s)-1 { - n += len(", ") - } - } - return n -} - -func (s1 textList) Equal(s2 textNode) bool { - if s2, ok := s2.(textList); ok { - if len(s1) != len(s2) { - return false - } - for i := range s1 { - r1, r2 := s1[i], s2[i] - if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { - return false - } - } - return true - } - return false -} - -func (s textList) String() string { - return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() -} - -func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { - s = append(textList(nil), s...) // Avoid mutating original - - // Determine whether we can collapse this list as a single line. - n0 := len(b) // Original buffer length - var multiLine bool - for i, r := range s { - if r.Diff == diffInserted || r.Diff == diffRemoved { - multiLine = true - } - b = append(b, r.Key...) - if r.Key != "" { - b = append(b, ": "...) - } - b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) - if _, ok := s[i].Value.(textLine); !ok { - multiLine = true - } - if r.Comment != nil { - multiLine = true - } - if i < len(s)-1 { - b = append(b, ", "...) - } - } - // Force multi-lined output when printing a removed/inserted node that - // is sufficiently long. - if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { - multiLine = true - } - if !multiLine { - return b, textLine(b[n0:]) - } - return b, s -} - -func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { - alignKeyLens := s.alignLens( - func(r textRecord) bool { - _, isLine := r.Value.(textLine) - return r.Key == "" || !isLine - }, - func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, - ) - alignValueLens := s.alignLens( - func(r textRecord) bool { - _, isLine := r.Value.(textLine) - return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil - }, - func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, - ) - - // Format lists of simple lists in a batched form. - // If the list is sequence of only textLine values, - // then batch multiple values on a single line. - var isSimple bool - for _, r := range s { - _, isLine := r.Value.(textLine) - isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil - if !isSimple { - break - } - } - if isSimple { - n++ - var batch []byte - emitBatch := func() { - if len(batch) > 0 { - b = n.appendIndent(append(b, '\n'), d) - b = append(b, bytes.TrimRight(batch, " ")...) - batch = batch[:0] - } - } - for _, r := range s { - line := r.Value.(textLine) - if len(batch)+len(line)+len(", ") > maxColumnLength { - emitBatch() - } - batch = append(batch, line...) - batch = append(batch, ", "...) - } - emitBatch() - n-- - return n.appendIndent(append(b, '\n'), d) - } - - // Format the list as a multi-lined output. - n++ - for i, r := range s { - b = n.appendIndent(append(b, '\n'), d|r.Diff) - if r.Key != "" { - b = append(b, r.Key+": "...) - } - b = alignKeyLens[i].appendChar(b, ' ') - - b = r.Value.formatExpandedTo(b, d|r.Diff, n) - if !r.ElideComma { - b = append(b, ',') - } - b = alignValueLens[i].appendChar(b, ' ') - - if r.Comment != nil { - b = append(b, " // "+r.Comment.String()...) - } - } - n-- - - return n.appendIndent(append(b, '\n'), d) -} - -func (s textList) alignLens( - skipFunc func(textRecord) bool, - lenFunc func(textRecord) int, -) []repeatCount { - var startIdx, endIdx, maxLen int - lens := make([]repeatCount, len(s)) - for i, r := range s { - if skipFunc(r) { - for j := startIdx; j < endIdx && j < len(s); j++ { - lens[j] = repeatCount(maxLen - lenFunc(s[j])) - } - startIdx, endIdx, maxLen = i+1, i+1, 0 - } else { - if maxLen < lenFunc(r) { - maxLen = lenFunc(r) - } - endIdx = i + 1 - } - } - for j := startIdx; j < endIdx && j < len(s); j++ { - lens[j] = repeatCount(maxLen - lenFunc(s[j])) - } - return lens -} - -// textLine is a single-line segment of text and is always a leaf node -// in the textNode tree. -type textLine []byte - -var ( - textNil = textLine("nil") - textEllipsis = textLine("...") -) - -func (s textLine) Len() int { - return len(s) -} -func (s1 textLine) Equal(s2 textNode) bool { - if s2, ok := s2.(textLine); ok { - return bytes.Equal([]byte(s1), []byte(s2)) - } - return false -} -func (s textLine) String() string { - return string(s) -} -func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { - return append(b, s...), s -} -func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { - return append(b, s...) -} - -type diffStats struct { - Name string - NumIgnored int - NumIdentical int - NumRemoved int - NumInserted int - NumModified int -} - -func (s diffStats) IsZero() bool { - s.Name = "" - return s == diffStats{} -} - -func (s diffStats) NumDiff() int { - return s.NumRemoved + s.NumInserted + s.NumModified -} - -func (s diffStats) Append(ds diffStats) diffStats { - assert(s.Name == ds.Name) - s.NumIgnored += ds.NumIgnored - s.NumIdentical += ds.NumIdentical - s.NumRemoved += ds.NumRemoved - s.NumInserted += ds.NumInserted - s.NumModified += ds.NumModified - return s -} - -// String prints a humanly-readable summary of coalesced records. -// -// Example: -// -// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" -func (s diffStats) String() string { - var ss []string - var sum int - labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} - counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} - for i, n := range counts { - if n > 0 { - ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) - } - sum += n - } - - // Pluralize the name (adjusting for some obscure English grammar rules). - name := s.Name - if sum > 1 { - name += "s" - if strings.HasSuffix(name, "ys") { - name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" - } - } - - // Format the list according to English grammar (with Oxford comma). - switch n := len(ss); n { - case 0: - return "" - case 1, 2: - return strings.Join(ss, " and ") + " " + name - default: - return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name - } -} - -type commentString string - -func (s commentString) String() string { return string(s) } diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go deleted file mode 100644 index 668d470f..00000000 --- a/vendor/github.com/google/go-cmp/cmp/report_value.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019, The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmp - -import "reflect" - -// valueNode represents a single node within a report, which is a -// structured representation of the value tree, containing information -// regarding which nodes are equal or not. -type valueNode struct { - parent *valueNode - - Type reflect.Type - ValueX reflect.Value - ValueY reflect.Value - - // NumSame is the number of leaf nodes that are equal. - // All descendants are equal only if NumDiff is 0. - NumSame int - // NumDiff is the number of leaf nodes that are not equal. - NumDiff int - // NumIgnored is the number of leaf nodes that are ignored. - NumIgnored int - // NumCompared is the number of leaf nodes that were compared - // using an Equal method or Comparer function. - NumCompared int - // NumTransformed is the number of non-leaf nodes that were transformed. - NumTransformed int - // NumChildren is the number of transitive descendants of this node. - // This counts from zero; thus, leaf nodes have no descendants. - NumChildren int - // MaxDepth is the maximum depth of the tree. This counts from zero; - // thus, leaf nodes have a depth of zero. - MaxDepth int - - // Records is a list of struct fields, slice elements, or map entries. - Records []reportRecord // If populated, implies Value is not populated - - // Value is the result of a transformation, pointer indirect, of - // type assertion. - Value *valueNode // If populated, implies Records is not populated - - // TransformerName is the name of the transformer. - TransformerName string // If non-empty, implies Value is populated -} -type reportRecord struct { - Key reflect.Value // Invalid for slice element - Value *valueNode -} - -func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { - vx, vy := ps.Values() - child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} - switch s := ps.(type) { - case StructField: - assert(parent.Value == nil) - parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) - case SliceIndex: - assert(parent.Value == nil) - parent.Records = append(parent.Records, reportRecord{Value: child}) - case MapIndex: - assert(parent.Value == nil) - parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) - case Indirect: - assert(parent.Value == nil && parent.Records == nil) - parent.Value = child - case TypeAssertion: - assert(parent.Value == nil && parent.Records == nil) - parent.Value = child - case Transform: - assert(parent.Value == nil && parent.Records == nil) - parent.Value = child - parent.TransformerName = s.Name() - parent.NumTransformed++ - default: - assert(parent == nil) // Must be the root step - } - return child -} - -func (r *valueNode) Report(rs Result) { - assert(r.MaxDepth == 0) // May only be called on leaf nodes - - if rs.ByIgnore() { - r.NumIgnored++ - } else { - if rs.Equal() { - r.NumSame++ - } else { - r.NumDiff++ - } - } - assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) - - if rs.ByMethod() { - r.NumCompared++ - } - if rs.ByFunc() { - r.NumCompared++ - } - assert(r.NumCompared <= 1) -} - -func (child *valueNode) PopStep() (parent *valueNode) { - if child.parent == nil { - return nil - } - parent = child.parent - parent.NumSame += child.NumSame - parent.NumDiff += child.NumDiff - parent.NumIgnored += child.NumIgnored - parent.NumCompared += child.NumCompared - parent.NumTransformed += child.NumTransformed - parent.NumChildren += child.NumChildren + 1 - if parent.MaxDepth < child.MaxDepth+1 { - parent.MaxDepth = child.MaxDepth + 1 - } - return parent -} diff --git a/vendor/github.com/google/gofuzz/.travis.yml b/vendor/github.com/google/gofuzz/.travis.yml deleted file mode 100644 index f8684d99..00000000 --- a/vendor/github.com/google/gofuzz/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.4 - - 1.3 - - 1.2 - - tip - -install: - - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi - -script: - - go test -cover diff --git a/vendor/github.com/google/gofuzz/CONTRIBUTING.md b/vendor/github.com/google/gofuzz/CONTRIBUTING.md deleted file mode 100644 index 51cf5cd1..00000000 --- a/vendor/github.com/google/gofuzz/CONTRIBUTING.md +++ /dev/null @@ -1,67 +0,0 @@ -# How to contribute # - -We'd love to accept your patches and contributions to this project. There are -a just a few small guidelines you need to follow. - - -## Contributor License Agreement ## - -Contributions to any Google project must be accompanied by a Contributor -License Agreement. This is not a copyright **assignment**, it simply gives -Google permission to use and redistribute your contributions as part of the -project. - - * If you are an individual writing original source code and you're sure you - own the intellectual property, then you'll need to sign an [individual - CLA][]. - - * If you work for a company that wants to allow you to contribute your work, - then you'll need to sign a [corporate CLA][]. - -You generally only need to submit a CLA once, so if you've already submitted -one (even if it was for a different project), you probably don't need to do it -again. - -[individual CLA]: https://developers.google.com/open-source/cla/individual -[corporate CLA]: https://developers.google.com/open-source/cla/corporate - - -## Submitting a patch ## - - 1. It's generally best to start by opening a new issue describing the bug or - feature you're intending to fix. Even if you think it's relatively minor, - it's helpful to know what people are working on. Mention in the initial - issue that you are planning to work on that bug or feature so that it can - be assigned to you. - - 1. Follow the normal process of [forking][] the project, and setup a new - branch to work in. It's important that each group of changes be done in - separate branches in order to ensure that a pull request only includes the - commits related to that bug or feature. - - 1. Go makes it very simple to ensure properly formatted code, so always run - `go fmt` on your code before committing it. You should also run - [golint][] over your code. As noted in the [golint readme][], it's not - strictly necessary that your code be completely "lint-free", but this will - help you find common style issues. - - 1. Any significant changes should almost always be accompanied by tests. The - project already has good test coverage, so look at some of the existing - tests if you're unsure how to go about it. [gocov][] and [gocov-html][] - are invaluable tools for seeing which parts of your code aren't being - exercised by your tests. - - 1. Do your best to have [well-formed commit messages][] for each change. - This provides consistency throughout the project, and ensures that commit - messages are able to be formatted properly by various git tools. - - 1. Finally, push the commits to your fork and submit a [pull request][]. - -[forking]: https://help.github.com/articles/fork-a-repo -[golint]: https://github.com/golang/lint -[golint readme]: https://github.com/golang/lint/blob/master/README -[gocov]: https://github.com/axw/gocov -[gocov-html]: https://github.com/matm/gocov-html -[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits -[pull request]: https://help.github.com/articles/creating-a-pull-request diff --git a/vendor/github.com/google/gofuzz/LICENSE b/vendor/github.com/google/gofuzz/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/github.com/google/gofuzz/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/github.com/google/gofuzz/README.md b/vendor/github.com/google/gofuzz/README.md deleted file mode 100644 index 64869af3..00000000 --- a/vendor/github.com/google/gofuzz/README.md +++ /dev/null @@ -1,71 +0,0 @@ -gofuzz -====== - -gofuzz is a library for populating go objects with random values. - -[![GoDoc](https://godoc.org/github.com/google/gofuzz?status.png)](https://godoc.org/github.com/google/gofuzz) -[![Travis](https://travis-ci.org/google/gofuzz.svg?branch=master)](https://travis-ci.org/google/gofuzz) - -This is useful for testing: - -* Do your project's objects really serialize/unserialize correctly in all cases? -* Is there an incorrectly formatted object that will cause your project to panic? - -Import with ```import "github.com/google/gofuzz"``` - -You can use it on single variables: -```go -f := fuzz.New() -var myInt int -f.Fuzz(&myInt) // myInt gets a random value. -``` - -You can use it on maps: -```go -f := fuzz.New().NilChance(0).NumElements(1, 1) -var myMap map[ComplexKeyType]string -f.Fuzz(&myMap) // myMap will have exactly one element. -``` - -Customize the chance of getting a nil pointer: -```go -f := fuzz.New().NilChance(.5) -var fancyStruct struct { - A, B, C, D *string -} -f.Fuzz(&fancyStruct) // About half the pointers should be set. -``` - -You can even customize the randomization completely if needed: -```go -type MyEnum string -const ( - A MyEnum = "A" - B MyEnum = "B" -) -type MyInfo struct { - Type MyEnum - AInfo *string - BInfo *string -} - -f := fuzz.New().NilChance(0).Funcs( - func(e *MyInfo, c fuzz.Continue) { - switch c.Intn(2) { - case 0: - e.Type = A - c.Fuzz(&e.AInfo) - case 1: - e.Type = B - c.Fuzz(&e.BInfo) - } - }, -) - -var myObject MyInfo -f.Fuzz(&myObject) // Type will correspond to whether A or B info is set. -``` - -See more examples in ```example_test.go```. - -Happy testing! diff --git a/vendor/github.com/google/gofuzz/doc.go b/vendor/github.com/google/gofuzz/doc.go deleted file mode 100644 index 9f9956d4..00000000 --- a/vendor/github.com/google/gofuzz/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 fuzz is a library for populating go objects with random values. -package fuzz diff --git a/vendor/github.com/google/gofuzz/fuzz.go b/vendor/github.com/google/gofuzz/fuzz.go deleted file mode 100644 index 1dfa80a6..00000000 --- a/vendor/github.com/google/gofuzz/fuzz.go +++ /dev/null @@ -1,487 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -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 fuzz - -import ( - "fmt" - "math/rand" - "reflect" - "time" -) - -// fuzzFuncMap is a map from a type to a fuzzFunc that handles that type. -type fuzzFuncMap map[reflect.Type]reflect.Value - -// Fuzzer knows how to fill any object with random fields. -type Fuzzer struct { - fuzzFuncs fuzzFuncMap - defaultFuzzFuncs fuzzFuncMap - r *rand.Rand - nilChance float64 - minElements int - maxElements int - maxDepth int -} - -// New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs, -// RandSource, NilChance, or NumElements in any order. -func New() *Fuzzer { - return NewWithSeed(time.Now().UnixNano()) -} - -func NewWithSeed(seed int64) *Fuzzer { - f := &Fuzzer{ - defaultFuzzFuncs: fuzzFuncMap{ - reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime), - }, - - fuzzFuncs: fuzzFuncMap{}, - r: rand.New(rand.NewSource(seed)), - nilChance: .2, - minElements: 1, - maxElements: 10, - maxDepth: 100, - } - return f -} - -// Funcs adds each entry in fuzzFuncs as a custom fuzzing function. -// -// Each entry in fuzzFuncs must be a function taking two parameters. -// The first parameter must be a pointer or map. It is the variable that -// function will fill with random data. The second parameter must be a -// fuzz.Continue, which will provide a source of randomness and a way -// to automatically continue fuzzing smaller pieces of the first parameter. -// -// These functions are called sensibly, e.g., if you wanted custom string -// fuzzing, the function `func(s *string, c fuzz.Continue)` would get -// called and passed the address of strings. Maps and pointers will always -// be made/new'd for you, ignoring the NilChange option. For slices, it -// doesn't make much sense to pre-create them--Fuzzer doesn't know how -// long you want your slice--so take a pointer to a slice, and make it -// yourself. (If you don't want your map/pointer type pre-made, take a -// pointer to it, and make it yourself.) See the examples for a range of -// custom functions. -func (f *Fuzzer) Funcs(fuzzFuncs ...interface{}) *Fuzzer { - for i := range fuzzFuncs { - v := reflect.ValueOf(fuzzFuncs[i]) - if v.Kind() != reflect.Func { - panic("Need only funcs!") - } - t := v.Type() - if t.NumIn() != 2 || t.NumOut() != 0 { - panic("Need 2 in and 0 out params!") - } - argT := t.In(0) - switch argT.Kind() { - case reflect.Ptr, reflect.Map: - default: - panic("fuzzFunc must take pointer or map type") - } - if t.In(1) != reflect.TypeOf(Continue{}) { - panic("fuzzFunc's second parameter must be type fuzz.Continue") - } - f.fuzzFuncs[argT] = v - } - return f -} - -// RandSource causes f to get values from the given source of randomness. -// Use if you want deterministic fuzzing. -func (f *Fuzzer) RandSource(s rand.Source) *Fuzzer { - f.r = rand.New(s) - return f -} - -// NilChance sets the probability of creating a nil pointer, map, or slice to -// 'p'. 'p' should be between 0 (no nils) and 1 (all nils), inclusive. -func (f *Fuzzer) NilChance(p float64) *Fuzzer { - if p < 0 || p > 1 { - panic("p should be between 0 and 1, inclusive.") - } - f.nilChance = p - return f -} - -// NumElements sets the minimum and maximum number of elements that will be -// added to a non-nil map or slice. -func (f *Fuzzer) NumElements(atLeast, atMost int) *Fuzzer { - if atLeast > atMost { - panic("atLeast must be <= atMost") - } - if atLeast < 0 { - panic("atLeast must be >= 0") - } - f.minElements = atLeast - f.maxElements = atMost - return f -} - -func (f *Fuzzer) genElementCount() int { - if f.minElements == f.maxElements { - return f.minElements - } - return f.minElements + f.r.Intn(f.maxElements-f.minElements+1) -} - -func (f *Fuzzer) genShouldFill() bool { - return f.r.Float64() > f.nilChance -} - -// MaxDepth sets the maximum number of recursive fuzz calls that will be made -// before stopping. This includes struct members, pointers, and map and slice -// elements. -func (f *Fuzzer) MaxDepth(d int) *Fuzzer { - f.maxDepth = d - return f -} - -// Fuzz recursively fills all of obj's fields with something random. First -// this tries to find a custom fuzz function (see Funcs). If there is no -// custom function this tests whether the object implements fuzz.Interface and, -// if so, calls Fuzz on it to fuzz itself. If that fails, this will see if -// there is a default fuzz function provided by this package. If all of that -// fails, this will generate random values for all primitive fields and then -// recurse for all non-primitives. -// -// This is safe for cyclic or tree-like structs, up to a limit. Use the -// MaxDepth method to adjust how deep you need it to recurse. -// -// obj must be a pointer. Only exported (public) fields can be set (thanks, -// golang :/ ) Intended for tests, so will panic on bad input or unimplemented -// fields. -func (f *Fuzzer) Fuzz(obj interface{}) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - panic("needed ptr!") - } - v = v.Elem() - f.fuzzWithContext(v, 0) -} - -// FuzzNoCustom is just like Fuzz, except that any custom fuzz function for -// obj's type will not be called and obj will not be tested for fuzz.Interface -// conformance. This applies only to obj and not other instances of obj's -// type. -// Not safe for cyclic or tree-like structs! -// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ ) -// Intended for tests, so will panic on bad input or unimplemented fields. -func (f *Fuzzer) FuzzNoCustom(obj interface{}) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - panic("needed ptr!") - } - v = v.Elem() - f.fuzzWithContext(v, flagNoCustomFuzz) -} - -const ( - // Do not try to find a custom fuzz function. Does not apply recursively. - flagNoCustomFuzz uint64 = 1 << iota -) - -func (f *Fuzzer) fuzzWithContext(v reflect.Value, flags uint64) { - fc := &fuzzerContext{fuzzer: f} - fc.doFuzz(v, flags) -} - -// fuzzerContext carries context about a single fuzzing run, which lets Fuzzer -// be thread-safe. -type fuzzerContext struct { - fuzzer *Fuzzer - curDepth int -} - -func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) { - if fc.curDepth >= fc.fuzzer.maxDepth { - return - } - fc.curDepth++ - defer func() { fc.curDepth-- }() - - if !v.CanSet() { - return - } - - if flags&flagNoCustomFuzz == 0 { - // Check for both pointer and non-pointer custom functions. - if v.CanAddr() && fc.tryCustom(v.Addr()) { - return - } - if fc.tryCustom(v) { - return - } - } - - if fn, ok := fillFuncMap[v.Kind()]; ok { - fn(v, fc.fuzzer.r) - return - } - switch v.Kind() { - case reflect.Map: - if fc.fuzzer.genShouldFill() { - v.Set(reflect.MakeMap(v.Type())) - n := fc.fuzzer.genElementCount() - for i := 0; i < n; i++ { - key := reflect.New(v.Type().Key()).Elem() - fc.doFuzz(key, 0) - val := reflect.New(v.Type().Elem()).Elem() - fc.doFuzz(val, 0) - v.SetMapIndex(key, val) - } - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Ptr: - if fc.fuzzer.genShouldFill() { - v.Set(reflect.New(v.Type().Elem())) - fc.doFuzz(v.Elem(), 0) - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Slice: - if fc.fuzzer.genShouldFill() { - n := fc.fuzzer.genElementCount() - v.Set(reflect.MakeSlice(v.Type(), n, n)) - for i := 0; i < n; i++ { - fc.doFuzz(v.Index(i), 0) - } - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Array: - if fc.fuzzer.genShouldFill() { - n := v.Len() - for i := 0; i < n; i++ { - fc.doFuzz(v.Index(i), 0) - } - return - } - v.Set(reflect.Zero(v.Type())) - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - fc.doFuzz(v.Field(i), 0) - } - case reflect.Chan: - fallthrough - case reflect.Func: - fallthrough - case reflect.Interface: - fallthrough - default: - panic(fmt.Sprintf("Can't handle %#v", v.Interface())) - } -} - -// tryCustom searches for custom handlers, and returns true iff it finds a match -// and successfully randomizes v. -func (fc *fuzzerContext) tryCustom(v reflect.Value) bool { - // First: see if we have a fuzz function for it. - doCustom, ok := fc.fuzzer.fuzzFuncs[v.Type()] - if !ok { - // Second: see if it can fuzz itself. - if v.CanInterface() { - intf := v.Interface() - if fuzzable, ok := intf.(Interface); ok { - fuzzable.Fuzz(Continue{fc: fc, Rand: fc.fuzzer.r}) - return true - } - } - // Finally: see if there is a default fuzz function. - doCustom, ok = fc.fuzzer.defaultFuzzFuncs[v.Type()] - if !ok { - return false - } - } - - switch v.Kind() { - case reflect.Ptr: - if v.IsNil() { - if !v.CanSet() { - return false - } - v.Set(reflect.New(v.Type().Elem())) - } - case reflect.Map: - if v.IsNil() { - if !v.CanSet() { - return false - } - v.Set(reflect.MakeMap(v.Type())) - } - default: - return false - } - - doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{ - fc: fc, - Rand: fc.fuzzer.r, - })}) - return true -} - -// Interface represents an object that knows how to fuzz itself. Any time we -// find a type that implements this interface we will delegate the act of -// fuzzing itself. -type Interface interface { - Fuzz(c Continue) -} - -// Continue can be passed to custom fuzzing functions to allow them to use -// the correct source of randomness and to continue fuzzing their members. -type Continue struct { - fc *fuzzerContext - - // For convenience, Continue implements rand.Rand via embedding. - // Use this for generating any randomness if you want your fuzzing - // to be repeatable for a given seed. - *rand.Rand -} - -// Fuzz continues fuzzing obj. obj must be a pointer. -func (c Continue) Fuzz(obj interface{}) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - panic("needed ptr!") - } - v = v.Elem() - c.fc.doFuzz(v, 0) -} - -// FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for -// obj's type will not be called and obj will not be tested for fuzz.Interface -// conformance. This applies only to obj and not other instances of obj's -// type. -func (c Continue) FuzzNoCustom(obj interface{}) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - panic("needed ptr!") - } - v = v.Elem() - c.fc.doFuzz(v, flagNoCustomFuzz) -} - -// RandString makes a random string up to 20 characters long. The returned string -// may include a variety of (valid) UTF-8 encodings. -func (c Continue) RandString() string { - return randString(c.Rand) -} - -// RandUint64 makes random 64 bit numbers. -// Weirdly, rand doesn't have a function that gives you 64 random bits. -func (c Continue) RandUint64() uint64 { - return randUint64(c.Rand) -} - -// RandBool returns true or false randomly. -func (c Continue) RandBool() bool { - return randBool(c.Rand) -} - -func fuzzInt(v reflect.Value, r *rand.Rand) { - v.SetInt(int64(randUint64(r))) -} - -func fuzzUint(v reflect.Value, r *rand.Rand) { - v.SetUint(randUint64(r)) -} - -func fuzzTime(t *time.Time, c Continue) { - var sec, nsec int64 - // Allow for about 1000 years of random time values, which keeps things - // like JSON parsing reasonably happy. - sec = c.Rand.Int63n(1000 * 365 * 24 * 60 * 60) - c.Fuzz(&nsec) - *t = time.Unix(sec, nsec) -} - -var fillFuncMap = map[reflect.Kind]func(reflect.Value, *rand.Rand){ - reflect.Bool: func(v reflect.Value, r *rand.Rand) { - v.SetBool(randBool(r)) - }, - reflect.Int: fuzzInt, - reflect.Int8: fuzzInt, - reflect.Int16: fuzzInt, - reflect.Int32: fuzzInt, - reflect.Int64: fuzzInt, - reflect.Uint: fuzzUint, - reflect.Uint8: fuzzUint, - reflect.Uint16: fuzzUint, - reflect.Uint32: fuzzUint, - reflect.Uint64: fuzzUint, - reflect.Uintptr: fuzzUint, - reflect.Float32: func(v reflect.Value, r *rand.Rand) { - v.SetFloat(float64(r.Float32())) - }, - reflect.Float64: func(v reflect.Value, r *rand.Rand) { - v.SetFloat(r.Float64()) - }, - reflect.Complex64: func(v reflect.Value, r *rand.Rand) { - panic("unimplemented") - }, - reflect.Complex128: func(v reflect.Value, r *rand.Rand) { - panic("unimplemented") - }, - reflect.String: func(v reflect.Value, r *rand.Rand) { - v.SetString(randString(r)) - }, - reflect.UnsafePointer: func(v reflect.Value, r *rand.Rand) { - panic("unimplemented") - }, -} - -// randBool returns true or false randomly. -func randBool(r *rand.Rand) bool { - if r.Int()&1 == 1 { - return true - } - return false -} - -type charRange struct { - first, last rune -} - -// choose returns a random unicode character from the given range, using the -// given randomness source. -func (r *charRange) choose(rand *rand.Rand) rune { - count := int64(r.last - r.first) - return r.first + rune(rand.Int63n(count)) -} - -var unicodeRanges = []charRange{ - {' ', '~'}, // ASCII characters - {'\u00a0', '\u02af'}, // Multi-byte encoded characters - {'\u4e00', '\u9fff'}, // Common CJK (even longer encodings) -} - -// randString makes a random string up to 20 characters long. The returned string -// may include a variety of (valid) UTF-8 encodings. -func randString(r *rand.Rand) string { - n := r.Intn(20) - runes := make([]rune, n) - for i := range runes { - runes[i] = unicodeRanges[r.Intn(len(unicodeRanges))].choose(r) - } - return string(runes) -} - -// randUint64 makes random 64 bit numbers. -// Weirdly, rand doesn't have a function that gives you 64 random bits. -func randUint64(r *rand.Rand) uint64 { - return uint64(r.Uint32())<<32 | uint64(r.Uint32()) -} diff --git a/vendor/github.com/google/gofuzz/go.mod b/vendor/github.com/google/gofuzz/go.mod deleted file mode 100644 index 8ec4fe9e..00000000 --- a/vendor/github.com/google/gofuzz/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/google/gofuzz - -go 1.12 diff --git a/vendor/github.com/json-iterator/go/.codecov.yml b/vendor/github.com/json-iterator/go/.codecov.yml deleted file mode 100644 index 955dc0be..00000000 --- a/vendor/github.com/json-iterator/go/.codecov.yml +++ /dev/null @@ -1,3 +0,0 @@ -ignore: - - "output_tests/.*" - diff --git a/vendor/github.com/json-iterator/go/.gitignore b/vendor/github.com/json-iterator/go/.gitignore deleted file mode 100644 index 15556530..00000000 --- a/vendor/github.com/json-iterator/go/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/vendor -/bug_test.go -/coverage.txt -/.idea diff --git a/vendor/github.com/json-iterator/go/.travis.yml b/vendor/github.com/json-iterator/go/.travis.yml deleted file mode 100644 index 449e67cd..00000000 --- a/vendor/github.com/json-iterator/go/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go - -go: - - 1.8.x - - 1.x - -before_install: - - go get -t -v ./... - -script: - - ./test.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/json-iterator/go/Gopkg.lock b/vendor/github.com/json-iterator/go/Gopkg.lock deleted file mode 100644 index c8a9fbb3..00000000 --- a/vendor/github.com/json-iterator/go/Gopkg.lock +++ /dev/null @@ -1,21 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - name = "github.com/modern-go/concurrent" - packages = ["."] - revision = "e0a39a4cb4216ea8db28e22a69f4ec25610d513a" - version = "1.0.0" - -[[projects]] - name = "github.com/modern-go/reflect2" - packages = ["."] - revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" - version = "1.0.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "ea54a775e5a354cb015502d2e7aa4b74230fc77e894f34a838b268c25ec8eeb8" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/json-iterator/go/Gopkg.toml b/vendor/github.com/json-iterator/go/Gopkg.toml deleted file mode 100644 index 313a0f88..00000000 --- a/vendor/github.com/json-iterator/go/Gopkg.toml +++ /dev/null @@ -1,26 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - -ignored = ["github.com/davecgh/go-spew*","github.com/google/gofuzz*","github.com/stretchr/testify*"] - -[[constraint]] - name = "github.com/modern-go/reflect2" - version = "1.0.1" diff --git a/vendor/github.com/json-iterator/go/LICENSE b/vendor/github.com/json-iterator/go/LICENSE deleted file mode 100644 index 2cf4f5ab..00000000 --- a/vendor/github.com/json-iterator/go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 json-iterator - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/json-iterator/go/README.md b/vendor/github.com/json-iterator/go/README.md deleted file mode 100644 index c589addf..00000000 --- a/vendor/github.com/json-iterator/go/README.md +++ /dev/null @@ -1,85 +0,0 @@ -[![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) -[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) -[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) -[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) -[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) -[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE) -[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) - -A high-performance 100% compatible drop-in replacement of "encoding/json" - -# Benchmark - -![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) - -Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go - -Raw Result (easyjson requires static code generation) - -| | ns/op | allocation bytes | allocation times | -| --------------- | ----------- | ---------------- | ---------------- | -| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | -| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | -| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | -| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | -| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | -| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | - -Always benchmark with your own workload. -The result depends heavily on the data input. - -# Usage - -100% compatibility with standard lib - -Replace - -```go -import "encoding/json" -json.Marshal(&data) -``` - -with - -```go -import jsoniter "github.com/json-iterator/go" - -var json = jsoniter.ConfigCompatibleWithStandardLibrary -json.Marshal(&data) -``` - -Replace - -```go -import "encoding/json" -json.Unmarshal(input, &data) -``` - -with - -```go -import jsoniter "github.com/json-iterator/go" - -var json = jsoniter.ConfigCompatibleWithStandardLibrary -json.Unmarshal(input, &data) -``` - -[More documentation](http://jsoniter.com/migrate-from-go-std.html) - -# How to get - -``` -go get github.com/json-iterator/go -``` - -# Contribution Welcomed ! - -Contributors - -- [thockin](https://github.com/thockin) -- [mattn](https://github.com/mattn) -- [cch123](https://github.com/cch123) -- [Oleg Shaldybin](https://github.com/olegshaldybin) -- [Jason Toffaletti](https://github.com/toffaletti) - -Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) diff --git a/vendor/github.com/json-iterator/go/adapter.go b/vendor/github.com/json-iterator/go/adapter.go deleted file mode 100644 index 92d2cc4a..00000000 --- a/vendor/github.com/json-iterator/go/adapter.go +++ /dev/null @@ -1,150 +0,0 @@ -package jsoniter - -import ( - "bytes" - "io" -) - -// RawMessage to make replace json with jsoniter -type RawMessage []byte - -// Unmarshal adapts to json/encoding Unmarshal API -// -// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. -// Refer to https://godoc.org/encoding/json#Unmarshal for more information -func Unmarshal(data []byte, v interface{}) error { - return ConfigDefault.Unmarshal(data, v) -} - -// UnmarshalFromString is a convenient method to read from string instead of []byte -func UnmarshalFromString(str string, v interface{}) error { - return ConfigDefault.UnmarshalFromString(str, v) -} - -// Get quick method to get value from deeply nested JSON structure -func Get(data []byte, path ...interface{}) Any { - return ConfigDefault.Get(data, path...) -} - -// Marshal adapts to json/encoding Marshal API -// -// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API -// Refer to https://godoc.org/encoding/json#Marshal for more information -func Marshal(v interface{}) ([]byte, error) { - return ConfigDefault.Marshal(v) -} - -// MarshalIndent same as json.MarshalIndent. Prefix is not supported. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - return ConfigDefault.MarshalIndent(v, prefix, indent) -} - -// MarshalToString convenient method to write as string instead of []byte -func MarshalToString(v interface{}) (string, error) { - return ConfigDefault.MarshalToString(v) -} - -// NewDecoder adapts to json/stream NewDecoder API. -// -// NewDecoder returns a new decoder that reads from r. -// -// Instead of a json/encoding Decoder, an Decoder is returned -// Refer to https://godoc.org/encoding/json#NewDecoder for more information -func NewDecoder(reader io.Reader) *Decoder { - return ConfigDefault.NewDecoder(reader) -} - -// Decoder reads and decodes JSON values from an input stream. -// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress) -type Decoder struct { - iter *Iterator -} - -// Decode decode JSON into interface{} -func (adapter *Decoder) Decode(obj interface{}) error { - if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil { - if !adapter.iter.loadMore() { - return io.EOF - } - } - adapter.iter.ReadVal(obj) - err := adapter.iter.Error - if err == io.EOF { - return nil - } - return adapter.iter.Error -} - -// More is there more? -func (adapter *Decoder) More() bool { - iter := adapter.iter - if iter.Error != nil { - return false - } - c := iter.nextToken() - if c == 0 { - return false - } - iter.unreadByte() - return c != ']' && c != '}' -} - -// Buffered remaining buffer -func (adapter *Decoder) Buffered() io.Reader { - remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail] - return bytes.NewReader(remaining) -} - -// UseNumber causes the Decoder to unmarshal a number into an interface{} as a -// Number instead of as a float64. -func (adapter *Decoder) UseNumber() { - cfg := adapter.iter.cfg.configBeforeFrozen - cfg.UseNumber = true - adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) -} - -// DisallowUnknownFields causes the Decoder to return an error when the destination -// is a struct and the input contains object keys which do not match any -// non-ignored, exported fields in the destination. -func (adapter *Decoder) DisallowUnknownFields() { - cfg := adapter.iter.cfg.configBeforeFrozen - cfg.DisallowUnknownFields = true - adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) -} - -// NewEncoder same as json.NewEncoder -func NewEncoder(writer io.Writer) *Encoder { - return ConfigDefault.NewEncoder(writer) -} - -// Encoder same as json.Encoder -type Encoder struct { - stream *Stream -} - -// Encode encode interface{} as JSON to io.Writer -func (adapter *Encoder) Encode(val interface{}) error { - adapter.stream.WriteVal(val) - adapter.stream.WriteRaw("\n") - adapter.stream.Flush() - return adapter.stream.Error -} - -// SetIndent set the indention. Prefix is not supported -func (adapter *Encoder) SetIndent(prefix, indent string) { - config := adapter.stream.cfg.configBeforeFrozen - config.IndentionStep = len(indent) - adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) -} - -// SetEscapeHTML escape html by default, set to false to disable -func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) { - config := adapter.stream.cfg.configBeforeFrozen - config.EscapeHTML = escapeHTML - adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) -} - -// Valid reports whether data is a valid JSON encoding. -func Valid(data []byte) bool { - return ConfigDefault.Valid(data) -} diff --git a/vendor/github.com/json-iterator/go/any.go b/vendor/github.com/json-iterator/go/any.go deleted file mode 100644 index f6b8aeab..00000000 --- a/vendor/github.com/json-iterator/go/any.go +++ /dev/null @@ -1,325 +0,0 @@ -package jsoniter - -import ( - "errors" - "fmt" - "github.com/modern-go/reflect2" - "io" - "reflect" - "strconv" - "unsafe" -) - -// Any generic object representation. -// The lazy json implementation holds []byte and parse lazily. -type Any interface { - LastError() error - ValueType() ValueType - MustBeValid() Any - ToBool() bool - ToInt() int - ToInt32() int32 - ToInt64() int64 - ToUint() uint - ToUint32() uint32 - ToUint64() uint64 - ToFloat32() float32 - ToFloat64() float64 - ToString() string - ToVal(val interface{}) - Get(path ...interface{}) Any - Size() int - Keys() []string - GetInterface() interface{} - WriteTo(stream *Stream) -} - -type baseAny struct{} - -func (any *baseAny) Get(path ...interface{}) Any { - return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} -} - -func (any *baseAny) Size() int { - return 0 -} - -func (any *baseAny) Keys() []string { - return []string{} -} - -func (any *baseAny) ToVal(obj interface{}) { - panic("not implemented") -} - -// WrapInt32 turn int32 into Any interface -func WrapInt32(val int32) Any { - return &int32Any{baseAny{}, val} -} - -// WrapInt64 turn int64 into Any interface -func WrapInt64(val int64) Any { - return &int64Any{baseAny{}, val} -} - -// WrapUint32 turn uint32 into Any interface -func WrapUint32(val uint32) Any { - return &uint32Any{baseAny{}, val} -} - -// WrapUint64 turn uint64 into Any interface -func WrapUint64(val uint64) Any { - return &uint64Any{baseAny{}, val} -} - -// WrapFloat64 turn float64 into Any interface -func WrapFloat64(val float64) Any { - return &floatAny{baseAny{}, val} -} - -// WrapString turn string into Any interface -func WrapString(val string) Any { - return &stringAny{baseAny{}, val} -} - -// Wrap turn a go object into Any interface -func Wrap(val interface{}) Any { - if val == nil { - return &nilAny{} - } - asAny, isAny := val.(Any) - if isAny { - return asAny - } - typ := reflect2.TypeOf(val) - switch typ.Kind() { - case reflect.Slice: - return wrapArray(val) - case reflect.Struct: - return wrapStruct(val) - case reflect.Map: - return wrapMap(val) - case reflect.String: - return WrapString(val.(string)) - case reflect.Int: - if strconv.IntSize == 32 { - return WrapInt32(int32(val.(int))) - } - return WrapInt64(int64(val.(int))) - case reflect.Int8: - return WrapInt32(int32(val.(int8))) - case reflect.Int16: - return WrapInt32(int32(val.(int16))) - case reflect.Int32: - return WrapInt32(val.(int32)) - case reflect.Int64: - return WrapInt64(val.(int64)) - case reflect.Uint: - if strconv.IntSize == 32 { - return WrapUint32(uint32(val.(uint))) - } - return WrapUint64(uint64(val.(uint))) - case reflect.Uintptr: - if ptrSize == 32 { - return WrapUint32(uint32(val.(uintptr))) - } - return WrapUint64(uint64(val.(uintptr))) - case reflect.Uint8: - return WrapUint32(uint32(val.(uint8))) - case reflect.Uint16: - return WrapUint32(uint32(val.(uint16))) - case reflect.Uint32: - return WrapUint32(uint32(val.(uint32))) - case reflect.Uint64: - return WrapUint64(val.(uint64)) - case reflect.Float32: - return WrapFloat64(float64(val.(float32))) - case reflect.Float64: - return WrapFloat64(val.(float64)) - case reflect.Bool: - if val.(bool) == true { - return &trueAny{} - } - return &falseAny{} - } - return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)} -} - -// ReadAny read next JSON element as an Any object. It is a better json.RawMessage. -func (iter *Iterator) ReadAny() Any { - return iter.readAny() -} - -func (iter *Iterator) readAny() Any { - c := iter.nextToken() - switch c { - case '"': - iter.unreadByte() - return &stringAny{baseAny{}, iter.ReadString()} - case 'n': - iter.skipThreeBytes('u', 'l', 'l') // null - return &nilAny{} - case 't': - iter.skipThreeBytes('r', 'u', 'e') // true - return &trueAny{} - case 'f': - iter.skipFourBytes('a', 'l', 's', 'e') // false - return &falseAny{} - case '{': - return iter.readObjectAny() - case '[': - return iter.readArrayAny() - case '-': - return iter.readNumberAny(false) - case 0: - return &invalidAny{baseAny{}, errors.New("input is empty")} - default: - return iter.readNumberAny(true) - } -} - -func (iter *Iterator) readNumberAny(positive bool) Any { - iter.startCapture(iter.head - 1) - iter.skipNumber() - lazyBuf := iter.stopCapture() - return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} -} - -func (iter *Iterator) readObjectAny() Any { - iter.startCapture(iter.head - 1) - iter.skipObject() - lazyBuf := iter.stopCapture() - return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} -} - -func (iter *Iterator) readArrayAny() Any { - iter.startCapture(iter.head - 1) - iter.skipArray() - lazyBuf := iter.stopCapture() - return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} -} - -func locateObjectField(iter *Iterator, target string) []byte { - var found []byte - iter.ReadObjectCB(func(iter *Iterator, field string) bool { - if field == target { - found = iter.SkipAndReturnBytes() - return false - } - iter.Skip() - return true - }) - return found -} - -func locateArrayElement(iter *Iterator, target int) []byte { - var found []byte - n := 0 - iter.ReadArrayCB(func(iter *Iterator) bool { - if n == target { - found = iter.SkipAndReturnBytes() - return false - } - iter.Skip() - n++ - return true - }) - return found -} - -func locatePath(iter *Iterator, path []interface{}) Any { - for i, pathKeyObj := range path { - switch pathKey := pathKeyObj.(type) { - case string: - valueBytes := locateObjectField(iter, pathKey) - if valueBytes == nil { - return newInvalidAny(path[i:]) - } - iter.ResetBytes(valueBytes) - case int: - valueBytes := locateArrayElement(iter, pathKey) - if valueBytes == nil { - return newInvalidAny(path[i:]) - } - iter.ResetBytes(valueBytes) - case int32: - if '*' == pathKey { - return iter.readAny().Get(path[i:]...) - } - return newInvalidAny(path[i:]) - default: - return newInvalidAny(path[i:]) - } - } - if iter.Error != nil && iter.Error != io.EOF { - return &invalidAny{baseAny{}, iter.Error} - } - return iter.readAny() -} - -var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem() - -func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder { - if typ == anyType { - return &directAnyCodec{} - } - if typ.Implements(anyType) { - return &anyCodec{ - valType: typ, - } - } - return nil -} - -func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder { - if typ == anyType { - return &directAnyCodec{} - } - if typ.Implements(anyType) { - return &anyCodec{ - valType: typ, - } - } - return nil -} - -type anyCodec struct { - valType reflect2.Type -} - -func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - panic("not implemented") -} - -func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := codec.valType.UnsafeIndirect(ptr) - any := obj.(Any) - any.WriteTo(stream) -} - -func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool { - obj := codec.valType.UnsafeIndirect(ptr) - any := obj.(Any) - return any.Size() == 0 -} - -type directAnyCodec struct { -} - -func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - *(*Any)(ptr) = iter.readAny() -} - -func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - any := *(*Any)(ptr) - if any == nil { - stream.WriteNil() - return - } - any.WriteTo(stream) -} - -func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool { - any := *(*Any)(ptr) - return any.Size() == 0 -} diff --git a/vendor/github.com/json-iterator/go/any_array.go b/vendor/github.com/json-iterator/go/any_array.go deleted file mode 100644 index 0449e9aa..00000000 --- a/vendor/github.com/json-iterator/go/any_array.go +++ /dev/null @@ -1,278 +0,0 @@ -package jsoniter - -import ( - "reflect" - "unsafe" -) - -type arrayLazyAny struct { - baseAny - cfg *frozenConfig - buf []byte - err error -} - -func (any *arrayLazyAny) ValueType() ValueType { - return ArrayValue -} - -func (any *arrayLazyAny) MustBeValid() Any { - return any -} - -func (any *arrayLazyAny) LastError() error { - return any.err -} - -func (any *arrayLazyAny) ToBool() bool { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - return iter.ReadArray() -} - -func (any *arrayLazyAny) ToInt() int { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToInt32() int32 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToInt64() int64 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToUint() uint { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToUint32() uint32 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToUint64() uint64 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToFloat32() float32 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToFloat64() float64 { - if any.ToBool() { - return 1 - } - return 0 -} - -func (any *arrayLazyAny) ToString() string { - return *(*string)(unsafe.Pointer(&any.buf)) -} - -func (any *arrayLazyAny) ToVal(val interface{}) { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadVal(val) -} - -func (any *arrayLazyAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - switch firstPath := path[0].(type) { - case int: - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - valueBytes := locateArrayElement(iter, firstPath) - if valueBytes == nil { - return newInvalidAny(path) - } - iter.ResetBytes(valueBytes) - return locatePath(iter, path[1:]) - case int32: - if '*' == firstPath { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - arr := make([]Any, 0) - iter.ReadArrayCB(func(iter *Iterator) bool { - found := iter.readAny().Get(path[1:]...) - if found.ValueType() != InvalidValue { - arr = append(arr, found) - } - return true - }) - return wrapArray(arr) - } - return newInvalidAny(path) - default: - return newInvalidAny(path) - } -} - -func (any *arrayLazyAny) Size() int { - size := 0 - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadArrayCB(func(iter *Iterator) bool { - size++ - iter.Skip() - return true - }) - return size -} - -func (any *arrayLazyAny) WriteTo(stream *Stream) { - stream.Write(any.buf) -} - -func (any *arrayLazyAny) GetInterface() interface{} { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - return iter.Read() -} - -type arrayAny struct { - baseAny - val reflect.Value -} - -func wrapArray(val interface{}) *arrayAny { - return &arrayAny{baseAny{}, reflect.ValueOf(val)} -} - -func (any *arrayAny) ValueType() ValueType { - return ArrayValue -} - -func (any *arrayAny) MustBeValid() Any { - return any -} - -func (any *arrayAny) LastError() error { - return nil -} - -func (any *arrayAny) ToBool() bool { - return any.val.Len() != 0 -} - -func (any *arrayAny) ToInt() int { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToInt32() int32 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToInt64() int64 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToUint() uint { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToUint32() uint32 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToUint64() uint64 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToFloat32() float32 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToFloat64() float64 { - if any.val.Len() == 0 { - return 0 - } - return 1 -} - -func (any *arrayAny) ToString() string { - str, _ := MarshalToString(any.val.Interface()) - return str -} - -func (any *arrayAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - switch firstPath := path[0].(type) { - case int: - if firstPath < 0 || firstPath >= any.val.Len() { - return newInvalidAny(path) - } - return Wrap(any.val.Index(firstPath).Interface()) - case int32: - if '*' == firstPath { - mappedAll := make([]Any, 0) - for i := 0; i < any.val.Len(); i++ { - mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...) - if mapped.ValueType() != InvalidValue { - mappedAll = append(mappedAll, mapped) - } - } - return wrapArray(mappedAll) - } - return newInvalidAny(path) - default: - return newInvalidAny(path) - } -} - -func (any *arrayAny) Size() int { - return any.val.Len() -} - -func (any *arrayAny) WriteTo(stream *Stream) { - stream.WriteVal(any.val) -} - -func (any *arrayAny) GetInterface() interface{} { - return any.val.Interface() -} diff --git a/vendor/github.com/json-iterator/go/any_bool.go b/vendor/github.com/json-iterator/go/any_bool.go deleted file mode 100644 index 9452324a..00000000 --- a/vendor/github.com/json-iterator/go/any_bool.go +++ /dev/null @@ -1,137 +0,0 @@ -package jsoniter - -type trueAny struct { - baseAny -} - -func (any *trueAny) LastError() error { - return nil -} - -func (any *trueAny) ToBool() bool { - return true -} - -func (any *trueAny) ToInt() int { - return 1 -} - -func (any *trueAny) ToInt32() int32 { - return 1 -} - -func (any *trueAny) ToInt64() int64 { - return 1 -} - -func (any *trueAny) ToUint() uint { - return 1 -} - -func (any *trueAny) ToUint32() uint32 { - return 1 -} - -func (any *trueAny) ToUint64() uint64 { - return 1 -} - -func (any *trueAny) ToFloat32() float32 { - return 1 -} - -func (any *trueAny) ToFloat64() float64 { - return 1 -} - -func (any *trueAny) ToString() string { - return "true" -} - -func (any *trueAny) WriteTo(stream *Stream) { - stream.WriteTrue() -} - -func (any *trueAny) Parse() *Iterator { - return nil -} - -func (any *trueAny) GetInterface() interface{} { - return true -} - -func (any *trueAny) ValueType() ValueType { - return BoolValue -} - -func (any *trueAny) MustBeValid() Any { - return any -} - -type falseAny struct { - baseAny -} - -func (any *falseAny) LastError() error { - return nil -} - -func (any *falseAny) ToBool() bool { - return false -} - -func (any *falseAny) ToInt() int { - return 0 -} - -func (any *falseAny) ToInt32() int32 { - return 0 -} - -func (any *falseAny) ToInt64() int64 { - return 0 -} - -func (any *falseAny) ToUint() uint { - return 0 -} - -func (any *falseAny) ToUint32() uint32 { - return 0 -} - -func (any *falseAny) ToUint64() uint64 { - return 0 -} - -func (any *falseAny) ToFloat32() float32 { - return 0 -} - -func (any *falseAny) ToFloat64() float64 { - return 0 -} - -func (any *falseAny) ToString() string { - return "false" -} - -func (any *falseAny) WriteTo(stream *Stream) { - stream.WriteFalse() -} - -func (any *falseAny) Parse() *Iterator { - return nil -} - -func (any *falseAny) GetInterface() interface{} { - return false -} - -func (any *falseAny) ValueType() ValueType { - return BoolValue -} - -func (any *falseAny) MustBeValid() Any { - return any -} diff --git a/vendor/github.com/json-iterator/go/any_float.go b/vendor/github.com/json-iterator/go/any_float.go deleted file mode 100644 index 35fdb094..00000000 --- a/vendor/github.com/json-iterator/go/any_float.go +++ /dev/null @@ -1,83 +0,0 @@ -package jsoniter - -import ( - "strconv" -) - -type floatAny struct { - baseAny - val float64 -} - -func (any *floatAny) Parse() *Iterator { - return nil -} - -func (any *floatAny) ValueType() ValueType { - return NumberValue -} - -func (any *floatAny) MustBeValid() Any { - return any -} - -func (any *floatAny) LastError() error { - return nil -} - -func (any *floatAny) ToBool() bool { - return any.ToFloat64() != 0 -} - -func (any *floatAny) ToInt() int { - return int(any.val) -} - -func (any *floatAny) ToInt32() int32 { - return int32(any.val) -} - -func (any *floatAny) ToInt64() int64 { - return int64(any.val) -} - -func (any *floatAny) ToUint() uint { - if any.val > 0 { - return uint(any.val) - } - return 0 -} - -func (any *floatAny) ToUint32() uint32 { - if any.val > 0 { - return uint32(any.val) - } - return 0 -} - -func (any *floatAny) ToUint64() uint64 { - if any.val > 0 { - return uint64(any.val) - } - return 0 -} - -func (any *floatAny) ToFloat32() float32 { - return float32(any.val) -} - -func (any *floatAny) ToFloat64() float64 { - return any.val -} - -func (any *floatAny) ToString() string { - return strconv.FormatFloat(any.val, 'E', -1, 64) -} - -func (any *floatAny) WriteTo(stream *Stream) { - stream.WriteFloat64(any.val) -} - -func (any *floatAny) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/any_int32.go b/vendor/github.com/json-iterator/go/any_int32.go deleted file mode 100644 index 1b56f399..00000000 --- a/vendor/github.com/json-iterator/go/any_int32.go +++ /dev/null @@ -1,74 +0,0 @@ -package jsoniter - -import ( - "strconv" -) - -type int32Any struct { - baseAny - val int32 -} - -func (any *int32Any) LastError() error { - return nil -} - -func (any *int32Any) ValueType() ValueType { - return NumberValue -} - -func (any *int32Any) MustBeValid() Any { - return any -} - -func (any *int32Any) ToBool() bool { - return any.val != 0 -} - -func (any *int32Any) ToInt() int { - return int(any.val) -} - -func (any *int32Any) ToInt32() int32 { - return any.val -} - -func (any *int32Any) ToInt64() int64 { - return int64(any.val) -} - -func (any *int32Any) ToUint() uint { - return uint(any.val) -} - -func (any *int32Any) ToUint32() uint32 { - return uint32(any.val) -} - -func (any *int32Any) ToUint64() uint64 { - return uint64(any.val) -} - -func (any *int32Any) ToFloat32() float32 { - return float32(any.val) -} - -func (any *int32Any) ToFloat64() float64 { - return float64(any.val) -} - -func (any *int32Any) ToString() string { - return strconv.FormatInt(int64(any.val), 10) -} - -func (any *int32Any) WriteTo(stream *Stream) { - stream.WriteInt32(any.val) -} - -func (any *int32Any) Parse() *Iterator { - return nil -} - -func (any *int32Any) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/any_int64.go b/vendor/github.com/json-iterator/go/any_int64.go deleted file mode 100644 index c440d72b..00000000 --- a/vendor/github.com/json-iterator/go/any_int64.go +++ /dev/null @@ -1,74 +0,0 @@ -package jsoniter - -import ( - "strconv" -) - -type int64Any struct { - baseAny - val int64 -} - -func (any *int64Any) LastError() error { - return nil -} - -func (any *int64Any) ValueType() ValueType { - return NumberValue -} - -func (any *int64Any) MustBeValid() Any { - return any -} - -func (any *int64Any) ToBool() bool { - return any.val != 0 -} - -func (any *int64Any) ToInt() int { - return int(any.val) -} - -func (any *int64Any) ToInt32() int32 { - return int32(any.val) -} - -func (any *int64Any) ToInt64() int64 { - return any.val -} - -func (any *int64Any) ToUint() uint { - return uint(any.val) -} - -func (any *int64Any) ToUint32() uint32 { - return uint32(any.val) -} - -func (any *int64Any) ToUint64() uint64 { - return uint64(any.val) -} - -func (any *int64Any) ToFloat32() float32 { - return float32(any.val) -} - -func (any *int64Any) ToFloat64() float64 { - return float64(any.val) -} - -func (any *int64Any) ToString() string { - return strconv.FormatInt(any.val, 10) -} - -func (any *int64Any) WriteTo(stream *Stream) { - stream.WriteInt64(any.val) -} - -func (any *int64Any) Parse() *Iterator { - return nil -} - -func (any *int64Any) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/any_invalid.go b/vendor/github.com/json-iterator/go/any_invalid.go deleted file mode 100644 index 1d859eac..00000000 --- a/vendor/github.com/json-iterator/go/any_invalid.go +++ /dev/null @@ -1,82 +0,0 @@ -package jsoniter - -import "fmt" - -type invalidAny struct { - baseAny - err error -} - -func newInvalidAny(path []interface{}) *invalidAny { - return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)} -} - -func (any *invalidAny) LastError() error { - return any.err -} - -func (any *invalidAny) ValueType() ValueType { - return InvalidValue -} - -func (any *invalidAny) MustBeValid() Any { - panic(any.err) -} - -func (any *invalidAny) ToBool() bool { - return false -} - -func (any *invalidAny) ToInt() int { - return 0 -} - -func (any *invalidAny) ToInt32() int32 { - return 0 -} - -func (any *invalidAny) ToInt64() int64 { - return 0 -} - -func (any *invalidAny) ToUint() uint { - return 0 -} - -func (any *invalidAny) ToUint32() uint32 { - return 0 -} - -func (any *invalidAny) ToUint64() uint64 { - return 0 -} - -func (any *invalidAny) ToFloat32() float32 { - return 0 -} - -func (any *invalidAny) ToFloat64() float64 { - return 0 -} - -func (any *invalidAny) ToString() string { - return "" -} - -func (any *invalidAny) WriteTo(stream *Stream) { -} - -func (any *invalidAny) Get(path ...interface{}) Any { - if any.err == nil { - return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)} - } - return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)} -} - -func (any *invalidAny) Parse() *Iterator { - return nil -} - -func (any *invalidAny) GetInterface() interface{} { - return nil -} diff --git a/vendor/github.com/json-iterator/go/any_nil.go b/vendor/github.com/json-iterator/go/any_nil.go deleted file mode 100644 index d04cb54c..00000000 --- a/vendor/github.com/json-iterator/go/any_nil.go +++ /dev/null @@ -1,69 +0,0 @@ -package jsoniter - -type nilAny struct { - baseAny -} - -func (any *nilAny) LastError() error { - return nil -} - -func (any *nilAny) ValueType() ValueType { - return NilValue -} - -func (any *nilAny) MustBeValid() Any { - return any -} - -func (any *nilAny) ToBool() bool { - return false -} - -func (any *nilAny) ToInt() int { - return 0 -} - -func (any *nilAny) ToInt32() int32 { - return 0 -} - -func (any *nilAny) ToInt64() int64 { - return 0 -} - -func (any *nilAny) ToUint() uint { - return 0 -} - -func (any *nilAny) ToUint32() uint32 { - return 0 -} - -func (any *nilAny) ToUint64() uint64 { - return 0 -} - -func (any *nilAny) ToFloat32() float32 { - return 0 -} - -func (any *nilAny) ToFloat64() float64 { - return 0 -} - -func (any *nilAny) ToString() string { - return "" -} - -func (any *nilAny) WriteTo(stream *Stream) { - stream.WriteNil() -} - -func (any *nilAny) Parse() *Iterator { - return nil -} - -func (any *nilAny) GetInterface() interface{} { - return nil -} diff --git a/vendor/github.com/json-iterator/go/any_number.go b/vendor/github.com/json-iterator/go/any_number.go deleted file mode 100644 index 9d1e901a..00000000 --- a/vendor/github.com/json-iterator/go/any_number.go +++ /dev/null @@ -1,123 +0,0 @@ -package jsoniter - -import ( - "io" - "unsafe" -) - -type numberLazyAny struct { - baseAny - cfg *frozenConfig - buf []byte - err error -} - -func (any *numberLazyAny) ValueType() ValueType { - return NumberValue -} - -func (any *numberLazyAny) MustBeValid() Any { - return any -} - -func (any *numberLazyAny) LastError() error { - return any.err -} - -func (any *numberLazyAny) ToBool() bool { - return any.ToFloat64() != 0 -} - -func (any *numberLazyAny) ToInt() int { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadInt() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToInt32() int32 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadInt32() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToInt64() int64 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadInt64() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToUint() uint { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadUint() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToUint32() uint32 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadUint32() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToUint64() uint64 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadUint64() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToFloat32() float32 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadFloat32() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToFloat64() float64 { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - val := iter.ReadFloat64() - if iter.Error != nil && iter.Error != io.EOF { - any.err = iter.Error - } - return val -} - -func (any *numberLazyAny) ToString() string { - return *(*string)(unsafe.Pointer(&any.buf)) -} - -func (any *numberLazyAny) WriteTo(stream *Stream) { - stream.Write(any.buf) -} - -func (any *numberLazyAny) GetInterface() interface{} { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - return iter.Read() -} diff --git a/vendor/github.com/json-iterator/go/any_object.go b/vendor/github.com/json-iterator/go/any_object.go deleted file mode 100644 index c44ef5c9..00000000 --- a/vendor/github.com/json-iterator/go/any_object.go +++ /dev/null @@ -1,374 +0,0 @@ -package jsoniter - -import ( - "reflect" - "unsafe" -) - -type objectLazyAny struct { - baseAny - cfg *frozenConfig - buf []byte - err error -} - -func (any *objectLazyAny) ValueType() ValueType { - return ObjectValue -} - -func (any *objectLazyAny) MustBeValid() Any { - return any -} - -func (any *objectLazyAny) LastError() error { - return any.err -} - -func (any *objectLazyAny) ToBool() bool { - return true -} - -func (any *objectLazyAny) ToInt() int { - return 0 -} - -func (any *objectLazyAny) ToInt32() int32 { - return 0 -} - -func (any *objectLazyAny) ToInt64() int64 { - return 0 -} - -func (any *objectLazyAny) ToUint() uint { - return 0 -} - -func (any *objectLazyAny) ToUint32() uint32 { - return 0 -} - -func (any *objectLazyAny) ToUint64() uint64 { - return 0 -} - -func (any *objectLazyAny) ToFloat32() float32 { - return 0 -} - -func (any *objectLazyAny) ToFloat64() float64 { - return 0 -} - -func (any *objectLazyAny) ToString() string { - return *(*string)(unsafe.Pointer(&any.buf)) -} - -func (any *objectLazyAny) ToVal(obj interface{}) { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadVal(obj) -} - -func (any *objectLazyAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - switch firstPath := path[0].(type) { - case string: - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - valueBytes := locateObjectField(iter, firstPath) - if valueBytes == nil { - return newInvalidAny(path) - } - iter.ResetBytes(valueBytes) - return locatePath(iter, path[1:]) - case int32: - if '*' == firstPath { - mappedAll := map[string]Any{} - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadMapCB(func(iter *Iterator, field string) bool { - mapped := locatePath(iter, path[1:]) - if mapped.ValueType() != InvalidValue { - mappedAll[field] = mapped - } - return true - }) - return wrapMap(mappedAll) - } - return newInvalidAny(path) - default: - return newInvalidAny(path) - } -} - -func (any *objectLazyAny) Keys() []string { - keys := []string{} - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadMapCB(func(iter *Iterator, field string) bool { - iter.Skip() - keys = append(keys, field) - return true - }) - return keys -} - -func (any *objectLazyAny) Size() int { - size := 0 - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - iter.ReadObjectCB(func(iter *Iterator, field string) bool { - iter.Skip() - size++ - return true - }) - return size -} - -func (any *objectLazyAny) WriteTo(stream *Stream) { - stream.Write(any.buf) -} - -func (any *objectLazyAny) GetInterface() interface{} { - iter := any.cfg.BorrowIterator(any.buf) - defer any.cfg.ReturnIterator(iter) - return iter.Read() -} - -type objectAny struct { - baseAny - err error - val reflect.Value -} - -func wrapStruct(val interface{}) *objectAny { - return &objectAny{baseAny{}, nil, reflect.ValueOf(val)} -} - -func (any *objectAny) ValueType() ValueType { - return ObjectValue -} - -func (any *objectAny) MustBeValid() Any { - return any -} - -func (any *objectAny) Parse() *Iterator { - return nil -} - -func (any *objectAny) LastError() error { - return any.err -} - -func (any *objectAny) ToBool() bool { - return any.val.NumField() != 0 -} - -func (any *objectAny) ToInt() int { - return 0 -} - -func (any *objectAny) ToInt32() int32 { - return 0 -} - -func (any *objectAny) ToInt64() int64 { - return 0 -} - -func (any *objectAny) ToUint() uint { - return 0 -} - -func (any *objectAny) ToUint32() uint32 { - return 0 -} - -func (any *objectAny) ToUint64() uint64 { - return 0 -} - -func (any *objectAny) ToFloat32() float32 { - return 0 -} - -func (any *objectAny) ToFloat64() float64 { - return 0 -} - -func (any *objectAny) ToString() string { - str, err := MarshalToString(any.val.Interface()) - any.err = err - return str -} - -func (any *objectAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - switch firstPath := path[0].(type) { - case string: - field := any.val.FieldByName(firstPath) - if !field.IsValid() { - return newInvalidAny(path) - } - return Wrap(field.Interface()) - case int32: - if '*' == firstPath { - mappedAll := map[string]Any{} - for i := 0; i < any.val.NumField(); i++ { - field := any.val.Field(i) - if field.CanInterface() { - mapped := Wrap(field.Interface()).Get(path[1:]...) - if mapped.ValueType() != InvalidValue { - mappedAll[any.val.Type().Field(i).Name] = mapped - } - } - } - return wrapMap(mappedAll) - } - return newInvalidAny(path) - default: - return newInvalidAny(path) - } -} - -func (any *objectAny) Keys() []string { - keys := make([]string, 0, any.val.NumField()) - for i := 0; i < any.val.NumField(); i++ { - keys = append(keys, any.val.Type().Field(i).Name) - } - return keys -} - -func (any *objectAny) Size() int { - return any.val.NumField() -} - -func (any *objectAny) WriteTo(stream *Stream) { - stream.WriteVal(any.val) -} - -func (any *objectAny) GetInterface() interface{} { - return any.val.Interface() -} - -type mapAny struct { - baseAny - err error - val reflect.Value -} - -func wrapMap(val interface{}) *mapAny { - return &mapAny{baseAny{}, nil, reflect.ValueOf(val)} -} - -func (any *mapAny) ValueType() ValueType { - return ObjectValue -} - -func (any *mapAny) MustBeValid() Any { - return any -} - -func (any *mapAny) Parse() *Iterator { - return nil -} - -func (any *mapAny) LastError() error { - return any.err -} - -func (any *mapAny) ToBool() bool { - return true -} - -func (any *mapAny) ToInt() int { - return 0 -} - -func (any *mapAny) ToInt32() int32 { - return 0 -} - -func (any *mapAny) ToInt64() int64 { - return 0 -} - -func (any *mapAny) ToUint() uint { - return 0 -} - -func (any *mapAny) ToUint32() uint32 { - return 0 -} - -func (any *mapAny) ToUint64() uint64 { - return 0 -} - -func (any *mapAny) ToFloat32() float32 { - return 0 -} - -func (any *mapAny) ToFloat64() float64 { - return 0 -} - -func (any *mapAny) ToString() string { - str, err := MarshalToString(any.val.Interface()) - any.err = err - return str -} - -func (any *mapAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - switch firstPath := path[0].(type) { - case int32: - if '*' == firstPath { - mappedAll := map[string]Any{} - for _, key := range any.val.MapKeys() { - keyAsStr := key.String() - element := Wrap(any.val.MapIndex(key).Interface()) - mapped := element.Get(path[1:]...) - if mapped.ValueType() != InvalidValue { - mappedAll[keyAsStr] = mapped - } - } - return wrapMap(mappedAll) - } - return newInvalidAny(path) - default: - value := any.val.MapIndex(reflect.ValueOf(firstPath)) - if !value.IsValid() { - return newInvalidAny(path) - } - return Wrap(value.Interface()) - } -} - -func (any *mapAny) Keys() []string { - keys := make([]string, 0, any.val.Len()) - for _, key := range any.val.MapKeys() { - keys = append(keys, key.String()) - } - return keys -} - -func (any *mapAny) Size() int { - return any.val.Len() -} - -func (any *mapAny) WriteTo(stream *Stream) { - stream.WriteVal(any.val) -} - -func (any *mapAny) GetInterface() interface{} { - return any.val.Interface() -} diff --git a/vendor/github.com/json-iterator/go/any_str.go b/vendor/github.com/json-iterator/go/any_str.go deleted file mode 100644 index 1f12f661..00000000 --- a/vendor/github.com/json-iterator/go/any_str.go +++ /dev/null @@ -1,166 +0,0 @@ -package jsoniter - -import ( - "fmt" - "strconv" -) - -type stringAny struct { - baseAny - val string -} - -func (any *stringAny) Get(path ...interface{}) Any { - if len(path) == 0 { - return any - } - return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} -} - -func (any *stringAny) Parse() *Iterator { - return nil -} - -func (any *stringAny) ValueType() ValueType { - return StringValue -} - -func (any *stringAny) MustBeValid() Any { - return any -} - -func (any *stringAny) LastError() error { - return nil -} - -func (any *stringAny) ToBool() bool { - str := any.ToString() - if str == "0" { - return false - } - for _, c := range str { - switch c { - case ' ', '\n', '\r', '\t': - default: - return true - } - } - return false -} - -func (any *stringAny) ToInt() int { - return int(any.ToInt64()) - -} - -func (any *stringAny) ToInt32() int32 { - return int32(any.ToInt64()) -} - -func (any *stringAny) ToInt64() int64 { - if any.val == "" { - return 0 - } - - flag := 1 - startPos := 0 - if any.val[0] == '+' || any.val[0] == '-' { - startPos = 1 - } - - if any.val[0] == '-' { - flag = -1 - } - - endPos := startPos - for i := startPos; i < len(any.val); i++ { - if any.val[i] >= '0' && any.val[i] <= '9' { - endPos = i + 1 - } else { - break - } - } - parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64) - return int64(flag) * parsed -} - -func (any *stringAny) ToUint() uint { - return uint(any.ToUint64()) -} - -func (any *stringAny) ToUint32() uint32 { - return uint32(any.ToUint64()) -} - -func (any *stringAny) ToUint64() uint64 { - if any.val == "" { - return 0 - } - - startPos := 0 - - if any.val[0] == '-' { - return 0 - } - if any.val[0] == '+' { - startPos = 1 - } - - endPos := startPos - for i := startPos; i < len(any.val); i++ { - if any.val[i] >= '0' && any.val[i] <= '9' { - endPos = i + 1 - } else { - break - } - } - parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64) - return parsed -} - -func (any *stringAny) ToFloat32() float32 { - return float32(any.ToFloat64()) -} - -func (any *stringAny) ToFloat64() float64 { - if len(any.val) == 0 { - return 0 - } - - // first char invalid - if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') { - return 0 - } - - // extract valid num expression from string - // eg 123true => 123, -12.12xxa => -12.12 - endPos := 1 - for i := 1; i < len(any.val); i++ { - if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' { - endPos = i + 1 - continue - } - - // end position is the first char which is not digit - if any.val[i] >= '0' && any.val[i] <= '9' { - endPos = i + 1 - } else { - endPos = i - break - } - } - parsed, _ := strconv.ParseFloat(any.val[:endPos], 64) - return parsed -} - -func (any *stringAny) ToString() string { - return any.val -} - -func (any *stringAny) WriteTo(stream *Stream) { - stream.WriteString(any.val) -} - -func (any *stringAny) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/any_uint32.go b/vendor/github.com/json-iterator/go/any_uint32.go deleted file mode 100644 index 656bbd33..00000000 --- a/vendor/github.com/json-iterator/go/any_uint32.go +++ /dev/null @@ -1,74 +0,0 @@ -package jsoniter - -import ( - "strconv" -) - -type uint32Any struct { - baseAny - val uint32 -} - -func (any *uint32Any) LastError() error { - return nil -} - -func (any *uint32Any) ValueType() ValueType { - return NumberValue -} - -func (any *uint32Any) MustBeValid() Any { - return any -} - -func (any *uint32Any) ToBool() bool { - return any.val != 0 -} - -func (any *uint32Any) ToInt() int { - return int(any.val) -} - -func (any *uint32Any) ToInt32() int32 { - return int32(any.val) -} - -func (any *uint32Any) ToInt64() int64 { - return int64(any.val) -} - -func (any *uint32Any) ToUint() uint { - return uint(any.val) -} - -func (any *uint32Any) ToUint32() uint32 { - return any.val -} - -func (any *uint32Any) ToUint64() uint64 { - return uint64(any.val) -} - -func (any *uint32Any) ToFloat32() float32 { - return float32(any.val) -} - -func (any *uint32Any) ToFloat64() float64 { - return float64(any.val) -} - -func (any *uint32Any) ToString() string { - return strconv.FormatInt(int64(any.val), 10) -} - -func (any *uint32Any) WriteTo(stream *Stream) { - stream.WriteUint32(any.val) -} - -func (any *uint32Any) Parse() *Iterator { - return nil -} - -func (any *uint32Any) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/any_uint64.go b/vendor/github.com/json-iterator/go/any_uint64.go deleted file mode 100644 index 7df2fce3..00000000 --- a/vendor/github.com/json-iterator/go/any_uint64.go +++ /dev/null @@ -1,74 +0,0 @@ -package jsoniter - -import ( - "strconv" -) - -type uint64Any struct { - baseAny - val uint64 -} - -func (any *uint64Any) LastError() error { - return nil -} - -func (any *uint64Any) ValueType() ValueType { - return NumberValue -} - -func (any *uint64Any) MustBeValid() Any { - return any -} - -func (any *uint64Any) ToBool() bool { - return any.val != 0 -} - -func (any *uint64Any) ToInt() int { - return int(any.val) -} - -func (any *uint64Any) ToInt32() int32 { - return int32(any.val) -} - -func (any *uint64Any) ToInt64() int64 { - return int64(any.val) -} - -func (any *uint64Any) ToUint() uint { - return uint(any.val) -} - -func (any *uint64Any) ToUint32() uint32 { - return uint32(any.val) -} - -func (any *uint64Any) ToUint64() uint64 { - return any.val -} - -func (any *uint64Any) ToFloat32() float32 { - return float32(any.val) -} - -func (any *uint64Any) ToFloat64() float64 { - return float64(any.val) -} - -func (any *uint64Any) ToString() string { - return strconv.FormatUint(any.val, 10) -} - -func (any *uint64Any) WriteTo(stream *Stream) { - stream.WriteUint64(any.val) -} - -func (any *uint64Any) Parse() *Iterator { - return nil -} - -func (any *uint64Any) GetInterface() interface{} { - return any.val -} diff --git a/vendor/github.com/json-iterator/go/build.sh b/vendor/github.com/json-iterator/go/build.sh deleted file mode 100644 index b45ef688..00000000 --- a/vendor/github.com/json-iterator/go/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e -set -x - -if [ ! -d /tmp/build-golang/src/github.com/json-iterator ]; then - mkdir -p /tmp/build-golang/src/github.com/json-iterator - ln -s $PWD /tmp/build-golang/src/github.com/json-iterator/go -fi -export GOPATH=/tmp/build-golang -go get -u github.com/golang/dep/cmd/dep -cd /tmp/build-golang/src/github.com/json-iterator/go -exec $GOPATH/bin/dep ensure -update diff --git a/vendor/github.com/json-iterator/go/config.go b/vendor/github.com/json-iterator/go/config.go deleted file mode 100644 index 2adcdc3b..00000000 --- a/vendor/github.com/json-iterator/go/config.go +++ /dev/null @@ -1,375 +0,0 @@ -package jsoniter - -import ( - "encoding/json" - "io" - "reflect" - "sync" - "unsafe" - - "github.com/modern-go/concurrent" - "github.com/modern-go/reflect2" -) - -// Config customize how the API should behave. -// The API is created from Config by Froze. -type Config struct { - IndentionStep int - MarshalFloatWith6Digits bool - EscapeHTML bool - SortMapKeys bool - UseNumber bool - DisallowUnknownFields bool - TagKey string - OnlyTaggedField bool - ValidateJsonRawMessage bool - ObjectFieldMustBeSimpleString bool - CaseSensitive bool -} - -// API the public interface of this package. -// Primary Marshal and Unmarshal. -type API interface { - IteratorPool - StreamPool - MarshalToString(v interface{}) (string, error) - Marshal(v interface{}) ([]byte, error) - MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) - UnmarshalFromString(str string, v interface{}) error - Unmarshal(data []byte, v interface{}) error - Get(data []byte, path ...interface{}) Any - NewEncoder(writer io.Writer) *Encoder - NewDecoder(reader io.Reader) *Decoder - Valid(data []byte) bool - RegisterExtension(extension Extension) - DecoderOf(typ reflect2.Type) ValDecoder - EncoderOf(typ reflect2.Type) ValEncoder -} - -// ConfigDefault the default API -var ConfigDefault = Config{ - EscapeHTML: true, -}.Froze() - -// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior -var ConfigCompatibleWithStandardLibrary = Config{ - EscapeHTML: true, - SortMapKeys: true, - ValidateJsonRawMessage: true, -}.Froze() - -// ConfigFastest marshals float with only 6 digits precision -var ConfigFastest = Config{ - EscapeHTML: false, - MarshalFloatWith6Digits: true, // will lose precession - ObjectFieldMustBeSimpleString: true, // do not unescape object field -}.Froze() - -type frozenConfig struct { - configBeforeFrozen Config - sortMapKeys bool - indentionStep int - objectFieldMustBeSimpleString bool - onlyTaggedField bool - disallowUnknownFields bool - decoderCache *concurrent.Map - encoderCache *concurrent.Map - encoderExtension Extension - decoderExtension Extension - extraExtensions []Extension - streamPool *sync.Pool - iteratorPool *sync.Pool - caseSensitive bool -} - -func (cfg *frozenConfig) initCache() { - cfg.decoderCache = concurrent.NewMap() - cfg.encoderCache = concurrent.NewMap() -} - -func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) { - cfg.decoderCache.Store(cacheKey, decoder) -} - -func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) { - cfg.encoderCache.Store(cacheKey, encoder) -} - -func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder { - decoder, found := cfg.decoderCache.Load(cacheKey) - if found { - return decoder.(ValDecoder) - } - return nil -} - -func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder { - encoder, found := cfg.encoderCache.Load(cacheKey) - if found { - return encoder.(ValEncoder) - } - return nil -} - -var cfgCache = concurrent.NewMap() - -func getFrozenConfigFromCache(cfg Config) *frozenConfig { - obj, found := cfgCache.Load(cfg) - if found { - return obj.(*frozenConfig) - } - return nil -} - -func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) { - cfgCache.Store(cfg, frozenConfig) -} - -// Froze forge API from config -func (cfg Config) Froze() API { - api := &frozenConfig{ - sortMapKeys: cfg.SortMapKeys, - indentionStep: cfg.IndentionStep, - objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString, - onlyTaggedField: cfg.OnlyTaggedField, - disallowUnknownFields: cfg.DisallowUnknownFields, - caseSensitive: cfg.CaseSensitive, - } - api.streamPool = &sync.Pool{ - New: func() interface{} { - return NewStream(api, nil, 512) - }, - } - api.iteratorPool = &sync.Pool{ - New: func() interface{} { - return NewIterator(api) - }, - } - api.initCache() - encoderExtension := EncoderExtension{} - decoderExtension := DecoderExtension{} - if cfg.MarshalFloatWith6Digits { - api.marshalFloatWith6Digits(encoderExtension) - } - if cfg.EscapeHTML { - api.escapeHTML(encoderExtension) - } - if cfg.UseNumber { - api.useNumber(decoderExtension) - } - if cfg.ValidateJsonRawMessage { - api.validateJsonRawMessage(encoderExtension) - } - api.encoderExtension = encoderExtension - api.decoderExtension = decoderExtension - api.configBeforeFrozen = cfg - return api -} - -func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig { - api := getFrozenConfigFromCache(cfg) - if api != nil { - return api - } - api = cfg.Froze().(*frozenConfig) - for _, extension := range extraExtensions { - api.RegisterExtension(extension) - } - addFrozenConfigToCache(cfg, api) - return api -} - -func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) { - encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { - rawMessage := *(*json.RawMessage)(ptr) - iter := cfg.BorrowIterator([]byte(rawMessage)) - defer cfg.ReturnIterator(iter) - iter.Read() - if iter.Error != nil && iter.Error != io.EOF { - stream.WriteRaw("null") - } else { - stream.WriteRaw(string(rawMessage)) - } - }, func(ptr unsafe.Pointer) bool { - return len(*((*json.RawMessage)(ptr))) == 0 - }} - extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder - extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder -} - -func (cfg *frozenConfig) useNumber(extension DecoderExtension) { - extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { - exitingValue := *((*interface{})(ptr)) - if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr { - iter.ReadVal(exitingValue) - return - } - if iter.WhatIsNext() == NumberValue { - *((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) - } else { - *((*interface{})(ptr)) = iter.Read() - } - }} -} -func (cfg *frozenConfig) getTagKey() string { - tagKey := cfg.configBeforeFrozen.TagKey - if tagKey == "" { - return "json" - } - return tagKey -} - -func (cfg *frozenConfig) RegisterExtension(extension Extension) { - cfg.extraExtensions = append(cfg.extraExtensions, extension) - copied := cfg.configBeforeFrozen - cfg.configBeforeFrozen = copied -} - -type lossyFloat32Encoder struct { -} - -func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteFloat32Lossy(*((*float32)(ptr))) -} - -func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool { - return *((*float32)(ptr)) == 0 -} - -type lossyFloat64Encoder struct { -} - -func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteFloat64Lossy(*((*float64)(ptr))) -} - -func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool { - return *((*float64)(ptr)) == 0 -} - -// EnableLossyFloatMarshalling keeps 10**(-6) precision -// for float variables for better performance. -func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) { - // for better performance - extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{} - extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{} -} - -type htmlEscapedStringEncoder struct { -} - -func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - str := *((*string)(ptr)) - stream.WriteStringWithHTMLEscaped(str) -} - -func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return *((*string)(ptr)) == "" -} - -func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) { - encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{} -} - -func (cfg *frozenConfig) cleanDecoders() { - typeDecoders = map[string]ValDecoder{} - fieldDecoders = map[string]ValDecoder{} - *cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig)) -} - -func (cfg *frozenConfig) cleanEncoders() { - typeEncoders = map[string]ValEncoder{} - fieldEncoders = map[string]ValEncoder{} - *cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig)) -} - -func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) { - stream := cfg.BorrowStream(nil) - defer cfg.ReturnStream(stream) - stream.WriteVal(v) - if stream.Error != nil { - return "", stream.Error - } - return string(stream.Buffer()), nil -} - -func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) { - stream := cfg.BorrowStream(nil) - defer cfg.ReturnStream(stream) - stream.WriteVal(v) - if stream.Error != nil { - return nil, stream.Error - } - result := stream.Buffer() - copied := make([]byte, len(result)) - copy(copied, result) - return copied, nil -} - -func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - if prefix != "" { - panic("prefix is not supported") - } - for _, r := range indent { - if r != ' ' { - panic("indent can only be space") - } - } - newCfg := cfg.configBeforeFrozen - newCfg.IndentionStep = len(indent) - return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v) -} - -func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error { - data := []byte(str) - iter := cfg.BorrowIterator(data) - defer cfg.ReturnIterator(iter) - iter.ReadVal(v) - c := iter.nextToken() - if c == 0 { - if iter.Error == io.EOF { - return nil - } - return iter.Error - } - iter.ReportError("Unmarshal", "there are bytes left after unmarshal") - return iter.Error -} - -func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any { - iter := cfg.BorrowIterator(data) - defer cfg.ReturnIterator(iter) - return locatePath(iter, path) -} - -func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error { - iter := cfg.BorrowIterator(data) - defer cfg.ReturnIterator(iter) - iter.ReadVal(v) - c := iter.nextToken() - if c == 0 { - if iter.Error == io.EOF { - return nil - } - return iter.Error - } - iter.ReportError("Unmarshal", "there are bytes left after unmarshal") - return iter.Error -} - -func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder { - stream := NewStream(cfg, writer, 512) - return &Encoder{stream} -} - -func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder { - iter := Parse(cfg, reader, 512) - return &Decoder{iter} -} - -func (cfg *frozenConfig) Valid(data []byte) bool { - iter := cfg.BorrowIterator(data) - defer cfg.ReturnIterator(iter) - iter.Skip() - return iter.Error == nil -} diff --git a/vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md b/vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md deleted file mode 100644 index 3095662b..00000000 --- a/vendor/github.com/json-iterator/go/fuzzy_mode_convert_table.md +++ /dev/null @@ -1,7 +0,0 @@ -| json type \ dest type | bool | int | uint | float |string| -| --- | --- | --- | --- |--|--| -| number | positive => true
negative => true
zero => false| 23.2 => 23
-32.1 => -32| 12.1 => 12
-12.1 => 0|as normal|same as origin| -| string | empty string => false
string "0" => false
other strings => true | "123.32" => 123
"-123.4" => -123
"123.23xxxw" => 123
"abcde12" => 0
"-32.1" => -32| 13.2 => 13
-1.1 => 0 |12.1 => 12.1
-12.3 => -12.3
12.4xxa => 12.4
+1.1e2 =>110 |same as origin| -| bool | true => true
false => false| true => 1
false => 0 | true => 1
false => 0 |true => 1
false => 0|true => "true"
false => "false"| -| object | true | 0 | 0 |0|originnal json| -| array | empty array => false
nonempty array => true| [] => 0
[1,2] => 1 | [] => 0
[1,2] => 1 |[] => 0
[1,2] => 1|original json| \ No newline at end of file diff --git a/vendor/github.com/json-iterator/go/go.mod b/vendor/github.com/json-iterator/go/go.mod deleted file mode 100644 index e817cccb..00000000 --- a/vendor/github.com/json-iterator/go/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/json-iterator/go - -go 1.12 - -require ( - github.com/davecgh/go-spew v1.1.1 - github.com/google/gofuzz v1.0.0 - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 - github.com/modern-go/reflect2 v1.0.2 - github.com/stretchr/testify v1.3.0 -) diff --git a/vendor/github.com/json-iterator/go/go.sum b/vendor/github.com/json-iterator/go/go.sum deleted file mode 100644 index 4b7bb8a2..00000000 --- a/vendor/github.com/json-iterator/go/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/json-iterator/go/iter.go b/vendor/github.com/json-iterator/go/iter.go deleted file mode 100644 index 29b31cf7..00000000 --- a/vendor/github.com/json-iterator/go/iter.go +++ /dev/null @@ -1,349 +0,0 @@ -package jsoniter - -import ( - "encoding/json" - "fmt" - "io" -) - -// ValueType the type for JSON element -type ValueType int - -const ( - // InvalidValue invalid JSON element - InvalidValue ValueType = iota - // StringValue JSON element "string" - StringValue - // NumberValue JSON element 100 or 0.10 - NumberValue - // NilValue JSON element null - NilValue - // BoolValue JSON element true or false - BoolValue - // ArrayValue JSON element [] - ArrayValue - // ObjectValue JSON element {} - ObjectValue -) - -var hexDigits []byte -var valueTypes []ValueType - -func init() { - hexDigits = make([]byte, 256) - for i := 0; i < len(hexDigits); i++ { - hexDigits[i] = 255 - } - for i := '0'; i <= '9'; i++ { - hexDigits[i] = byte(i - '0') - } - for i := 'a'; i <= 'f'; i++ { - hexDigits[i] = byte((i - 'a') + 10) - } - for i := 'A'; i <= 'F'; i++ { - hexDigits[i] = byte((i - 'A') + 10) - } - valueTypes = make([]ValueType, 256) - for i := 0; i < len(valueTypes); i++ { - valueTypes[i] = InvalidValue - } - valueTypes['"'] = StringValue - valueTypes['-'] = NumberValue - valueTypes['0'] = NumberValue - valueTypes['1'] = NumberValue - valueTypes['2'] = NumberValue - valueTypes['3'] = NumberValue - valueTypes['4'] = NumberValue - valueTypes['5'] = NumberValue - valueTypes['6'] = NumberValue - valueTypes['7'] = NumberValue - valueTypes['8'] = NumberValue - valueTypes['9'] = NumberValue - valueTypes['t'] = BoolValue - valueTypes['f'] = BoolValue - valueTypes['n'] = NilValue - valueTypes['['] = ArrayValue - valueTypes['{'] = ObjectValue -} - -// Iterator is a io.Reader like object, with JSON specific read functions. -// Error is not returned as return value, but stored as Error member on this iterator instance. -type Iterator struct { - cfg *frozenConfig - reader io.Reader - buf []byte - head int - tail int - depth int - captureStartedAt int - captured []byte - Error error - Attachment interface{} // open for customized decoder -} - -// NewIterator creates an empty Iterator instance -func NewIterator(cfg API) *Iterator { - return &Iterator{ - cfg: cfg.(*frozenConfig), - reader: nil, - buf: nil, - head: 0, - tail: 0, - depth: 0, - } -} - -// Parse creates an Iterator instance from io.Reader -func Parse(cfg API, reader io.Reader, bufSize int) *Iterator { - return &Iterator{ - cfg: cfg.(*frozenConfig), - reader: reader, - buf: make([]byte, bufSize), - head: 0, - tail: 0, - depth: 0, - } -} - -// ParseBytes creates an Iterator instance from byte array -func ParseBytes(cfg API, input []byte) *Iterator { - return &Iterator{ - cfg: cfg.(*frozenConfig), - reader: nil, - buf: input, - head: 0, - tail: len(input), - depth: 0, - } -} - -// ParseString creates an Iterator instance from string -func ParseString(cfg API, input string) *Iterator { - return ParseBytes(cfg, []byte(input)) -} - -// Pool returns a pool can provide more iterator with same configuration -func (iter *Iterator) Pool() IteratorPool { - return iter.cfg -} - -// Reset reuse iterator instance by specifying another reader -func (iter *Iterator) Reset(reader io.Reader) *Iterator { - iter.reader = reader - iter.head = 0 - iter.tail = 0 - iter.depth = 0 - return iter -} - -// ResetBytes reuse iterator instance by specifying another byte array as input -func (iter *Iterator) ResetBytes(input []byte) *Iterator { - iter.reader = nil - iter.buf = input - iter.head = 0 - iter.tail = len(input) - iter.depth = 0 - return iter -} - -// WhatIsNext gets ValueType of relatively next json element -func (iter *Iterator) WhatIsNext() ValueType { - valueType := valueTypes[iter.nextToken()] - iter.unreadByte() - return valueType -} - -func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - switch c { - case ' ', '\n', '\t', '\r': - continue - } - iter.head = i - return false - } - return true -} - -func (iter *Iterator) isObjectEnd() bool { - c := iter.nextToken() - if c == ',' { - return false - } - if c == '}' { - return true - } - iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c})) - return true -} - -func (iter *Iterator) nextToken() byte { - // a variation of skip whitespaces, returning the next non-whitespace token - for { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - switch c { - case ' ', '\n', '\t', '\r': - continue - } - iter.head = i + 1 - return c - } - if !iter.loadMore() { - return 0 - } - } -} - -// ReportError record a error in iterator instance with current position. -func (iter *Iterator) ReportError(operation string, msg string) { - if iter.Error != nil { - if iter.Error != io.EOF { - return - } - } - peekStart := iter.head - 10 - if peekStart < 0 { - peekStart = 0 - } - peekEnd := iter.head + 10 - if peekEnd > iter.tail { - peekEnd = iter.tail - } - parsing := string(iter.buf[peekStart:peekEnd]) - contextStart := iter.head - 50 - if contextStart < 0 { - contextStart = 0 - } - contextEnd := iter.head + 50 - if contextEnd > iter.tail { - contextEnd = iter.tail - } - context := string(iter.buf[contextStart:contextEnd]) - iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...", - operation, msg, iter.head-peekStart, parsing, context) -} - -// CurrentBuffer gets current buffer as string for debugging purpose -func (iter *Iterator) CurrentBuffer() string { - peekStart := iter.head - 10 - if peekStart < 0 { - peekStart = 0 - } - return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head, - string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) -} - -func (iter *Iterator) readByte() (ret byte) { - if iter.head == iter.tail { - if iter.loadMore() { - ret = iter.buf[iter.head] - iter.head++ - return ret - } - return 0 - } - ret = iter.buf[iter.head] - iter.head++ - return ret -} - -func (iter *Iterator) loadMore() bool { - if iter.reader == nil { - if iter.Error == nil { - iter.head = iter.tail - iter.Error = io.EOF - } - return false - } - if iter.captured != nil { - iter.captured = append(iter.captured, - iter.buf[iter.captureStartedAt:iter.tail]...) - iter.captureStartedAt = 0 - } - for { - n, err := iter.reader.Read(iter.buf) - if n == 0 { - if err != nil { - if iter.Error == nil { - iter.Error = err - } - return false - } - } else { - iter.head = 0 - iter.tail = n - return true - } - } -} - -func (iter *Iterator) unreadByte() { - if iter.Error != nil { - return - } - iter.head-- - return -} - -// Read read the next JSON element as generic interface{}. -func (iter *Iterator) Read() interface{} { - valueType := iter.WhatIsNext() - switch valueType { - case StringValue: - return iter.ReadString() - case NumberValue: - if iter.cfg.configBeforeFrozen.UseNumber { - return json.Number(iter.readNumberAsString()) - } - return iter.ReadFloat64() - case NilValue: - iter.skipFourBytes('n', 'u', 'l', 'l') - return nil - case BoolValue: - return iter.ReadBool() - case ArrayValue: - arr := []interface{}{} - iter.ReadArrayCB(func(iter *Iterator) bool { - var elem interface{} - iter.ReadVal(&elem) - arr = append(arr, elem) - return true - }) - return arr - case ObjectValue: - obj := map[string]interface{}{} - iter.ReadMapCB(func(Iter *Iterator, field string) bool { - var elem interface{} - iter.ReadVal(&elem) - obj[field] = elem - return true - }) - return obj - default: - iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType)) - return nil - } -} - -// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9 -const maxDepth = 10000 - -func (iter *Iterator) incrementDepth() (success bool) { - iter.depth++ - if iter.depth <= maxDepth { - return true - } - iter.ReportError("incrementDepth", "exceeded max depth") - return false -} - -func (iter *Iterator) decrementDepth() (success bool) { - iter.depth-- - if iter.depth >= 0 { - return true - } - iter.ReportError("decrementDepth", "unexpected negative nesting") - return false -} diff --git a/vendor/github.com/json-iterator/go/iter_array.go b/vendor/github.com/json-iterator/go/iter_array.go deleted file mode 100644 index 204fe0e0..00000000 --- a/vendor/github.com/json-iterator/go/iter_array.go +++ /dev/null @@ -1,64 +0,0 @@ -package jsoniter - -// ReadArray read array element, tells if the array has more element to read. -func (iter *Iterator) ReadArray() (ret bool) { - c := iter.nextToken() - switch c { - case 'n': - iter.skipThreeBytes('u', 'l', 'l') - return false // null - case '[': - c = iter.nextToken() - if c != ']' { - iter.unreadByte() - return true - } - return false - case ']': - return false - case ',': - return true - default: - iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c})) - return - } -} - -// ReadArrayCB read array with callback -func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) { - c := iter.nextToken() - if c == '[' { - if !iter.incrementDepth() { - return false - } - c = iter.nextToken() - if c != ']' { - iter.unreadByte() - if !callback(iter) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - for c == ',' { - if !callback(iter) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - } - if c != ']' { - iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c})) - iter.decrementDepth() - return false - } - return iter.decrementDepth() - } - return iter.decrementDepth() - } - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return true // null - } - iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c})) - return false -} diff --git a/vendor/github.com/json-iterator/go/iter_float.go b/vendor/github.com/json-iterator/go/iter_float.go deleted file mode 100644 index 8a3d8b6f..00000000 --- a/vendor/github.com/json-iterator/go/iter_float.go +++ /dev/null @@ -1,342 +0,0 @@ -package jsoniter - -import ( - "encoding/json" - "io" - "math/big" - "strconv" - "strings" - "unsafe" -) - -var floatDigits []int8 - -const invalidCharForNumber = int8(-1) -const endOfNumber = int8(-2) -const dotInNumber = int8(-3) - -func init() { - floatDigits = make([]int8, 256) - for i := 0; i < len(floatDigits); i++ { - floatDigits[i] = invalidCharForNumber - } - for i := int8('0'); i <= int8('9'); i++ { - floatDigits[i] = i - int8('0') - } - floatDigits[','] = endOfNumber - floatDigits[']'] = endOfNumber - floatDigits['}'] = endOfNumber - floatDigits[' '] = endOfNumber - floatDigits['\t'] = endOfNumber - floatDigits['\n'] = endOfNumber - floatDigits['.'] = dotInNumber -} - -// ReadBigFloat read big.Float -func (iter *Iterator) ReadBigFloat() (ret *big.Float) { - str := iter.readNumberAsString() - if iter.Error != nil && iter.Error != io.EOF { - return nil - } - prec := 64 - if len(str) > prec { - prec = len(str) - } - val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero) - if err != nil { - iter.Error = err - return nil - } - return val -} - -// ReadBigInt read big.Int -func (iter *Iterator) ReadBigInt() (ret *big.Int) { - str := iter.readNumberAsString() - if iter.Error != nil && iter.Error != io.EOF { - return nil - } - ret = big.NewInt(0) - var success bool - ret, success = ret.SetString(str, 10) - if !success { - iter.ReportError("ReadBigInt", "invalid big int") - return nil - } - return ret -} - -//ReadFloat32 read float32 -func (iter *Iterator) ReadFloat32() (ret float32) { - c := iter.nextToken() - if c == '-' { - return -iter.readPositiveFloat32() - } - iter.unreadByte() - return iter.readPositiveFloat32() -} - -func (iter *Iterator) readPositiveFloat32() (ret float32) { - i := iter.head - // first char - if i == iter.tail { - return iter.readFloat32SlowPath() - } - c := iter.buf[i] - i++ - ind := floatDigits[c] - switch ind { - case invalidCharForNumber: - return iter.readFloat32SlowPath() - case endOfNumber: - iter.ReportError("readFloat32", "empty number") - return - case dotInNumber: - iter.ReportError("readFloat32", "leading dot is invalid") - return - case 0: - if i == iter.tail { - return iter.readFloat32SlowPath() - } - c = iter.buf[i] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - iter.ReportError("readFloat32", "leading zero is invalid") - return - } - } - value := uint64(ind) - // chars before dot -non_decimal_loop: - for ; i < iter.tail; i++ { - c = iter.buf[i] - ind := floatDigits[c] - switch ind { - case invalidCharForNumber: - return iter.readFloat32SlowPath() - case endOfNumber: - iter.head = i - return float32(value) - case dotInNumber: - break non_decimal_loop - } - if value > uint64SafeToMultiple10 { - return iter.readFloat32SlowPath() - } - value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; - } - // chars after dot - if c == '.' { - i++ - decimalPlaces := 0 - if i == iter.tail { - return iter.readFloat32SlowPath() - } - for ; i < iter.tail; i++ { - c = iter.buf[i] - ind := floatDigits[c] - switch ind { - case endOfNumber: - if decimalPlaces > 0 && decimalPlaces < len(pow10) { - iter.head = i - return float32(float64(value) / float64(pow10[decimalPlaces])) - } - // too many decimal places - return iter.readFloat32SlowPath() - case invalidCharForNumber, dotInNumber: - return iter.readFloat32SlowPath() - } - decimalPlaces++ - if value > uint64SafeToMultiple10 { - return iter.readFloat32SlowPath() - } - value = (value << 3) + (value << 1) + uint64(ind) - } - } - return iter.readFloat32SlowPath() -} - -func (iter *Iterator) readNumberAsString() (ret string) { - strBuf := [16]byte{} - str := strBuf[0:0] -load_loop: - for { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - switch c { - case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - str = append(str, c) - continue - default: - iter.head = i - break load_loop - } - } - if !iter.loadMore() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF { - return - } - if len(str) == 0 { - iter.ReportError("readNumberAsString", "invalid number") - } - return *(*string)(unsafe.Pointer(&str)) -} - -func (iter *Iterator) readFloat32SlowPath() (ret float32) { - str := iter.readNumberAsString() - if iter.Error != nil && iter.Error != io.EOF { - return - } - errMsg := validateFloat(str) - if errMsg != "" { - iter.ReportError("readFloat32SlowPath", errMsg) - return - } - val, err := strconv.ParseFloat(str, 32) - if err != nil { - iter.Error = err - return - } - return float32(val) -} - -// ReadFloat64 read float64 -func (iter *Iterator) ReadFloat64() (ret float64) { - c := iter.nextToken() - if c == '-' { - return -iter.readPositiveFloat64() - } - iter.unreadByte() - return iter.readPositiveFloat64() -} - -func (iter *Iterator) readPositiveFloat64() (ret float64) { - i := iter.head - // first char - if i == iter.tail { - return iter.readFloat64SlowPath() - } - c := iter.buf[i] - i++ - ind := floatDigits[c] - switch ind { - case invalidCharForNumber: - return iter.readFloat64SlowPath() - case endOfNumber: - iter.ReportError("readFloat64", "empty number") - return - case dotInNumber: - iter.ReportError("readFloat64", "leading dot is invalid") - return - case 0: - if i == iter.tail { - return iter.readFloat64SlowPath() - } - c = iter.buf[i] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - iter.ReportError("readFloat64", "leading zero is invalid") - return - } - } - value := uint64(ind) - // chars before dot -non_decimal_loop: - for ; i < iter.tail; i++ { - c = iter.buf[i] - ind := floatDigits[c] - switch ind { - case invalidCharForNumber: - return iter.readFloat64SlowPath() - case endOfNumber: - iter.head = i - return float64(value) - case dotInNumber: - break non_decimal_loop - } - if value > uint64SafeToMultiple10 { - return iter.readFloat64SlowPath() - } - value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; - } - // chars after dot - if c == '.' { - i++ - decimalPlaces := 0 - if i == iter.tail { - return iter.readFloat64SlowPath() - } - for ; i < iter.tail; i++ { - c = iter.buf[i] - ind := floatDigits[c] - switch ind { - case endOfNumber: - if decimalPlaces > 0 && decimalPlaces < len(pow10) { - iter.head = i - return float64(value) / float64(pow10[decimalPlaces]) - } - // too many decimal places - return iter.readFloat64SlowPath() - case invalidCharForNumber, dotInNumber: - return iter.readFloat64SlowPath() - } - decimalPlaces++ - if value > uint64SafeToMultiple10 { - return iter.readFloat64SlowPath() - } - value = (value << 3) + (value << 1) + uint64(ind) - if value > maxFloat64 { - return iter.readFloat64SlowPath() - } - } - } - return iter.readFloat64SlowPath() -} - -func (iter *Iterator) readFloat64SlowPath() (ret float64) { - str := iter.readNumberAsString() - if iter.Error != nil && iter.Error != io.EOF { - return - } - errMsg := validateFloat(str) - if errMsg != "" { - iter.ReportError("readFloat64SlowPath", errMsg) - return - } - val, err := strconv.ParseFloat(str, 64) - if err != nil { - iter.Error = err - return - } - return val -} - -func validateFloat(str string) string { - // strconv.ParseFloat is not validating `1.` or `1.e1` - if len(str) == 0 { - return "empty number" - } - if str[0] == '-' { - return "-- is not valid" - } - dotPos := strings.IndexByte(str, '.') - if dotPos != -1 { - if dotPos == len(str)-1 { - return "dot can not be last character" - } - switch str[dotPos+1] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - default: - return "missing digit after dot" - } - } - return "" -} - -// ReadNumber read json.Number -func (iter *Iterator) ReadNumber() (ret json.Number) { - return json.Number(iter.readNumberAsString()) -} diff --git a/vendor/github.com/json-iterator/go/iter_int.go b/vendor/github.com/json-iterator/go/iter_int.go deleted file mode 100644 index d786a89f..00000000 --- a/vendor/github.com/json-iterator/go/iter_int.go +++ /dev/null @@ -1,346 +0,0 @@ -package jsoniter - -import ( - "math" - "strconv" -) - -var intDigits []int8 - -const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1 -const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1 -const maxFloat64 = 1<<53 - 1 - -func init() { - intDigits = make([]int8, 256) - for i := 0; i < len(intDigits); i++ { - intDigits[i] = invalidCharForNumber - } - for i := int8('0'); i <= int8('9'); i++ { - intDigits[i] = i - int8('0') - } -} - -// ReadUint read uint -func (iter *Iterator) ReadUint() uint { - if strconv.IntSize == 32 { - return uint(iter.ReadUint32()) - } - return uint(iter.ReadUint64()) -} - -// ReadInt read int -func (iter *Iterator) ReadInt() int { - if strconv.IntSize == 32 { - return int(iter.ReadInt32()) - } - return int(iter.ReadInt64()) -} - -// ReadInt8 read int8 -func (iter *Iterator) ReadInt8() (ret int8) { - c := iter.nextToken() - if c == '-' { - val := iter.readUint32(iter.readByte()) - if val > math.MaxInt8+1 { - iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return -int8(val) - } - val := iter.readUint32(c) - if val > math.MaxInt8 { - iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return int8(val) -} - -// ReadUint8 read uint8 -func (iter *Iterator) ReadUint8() (ret uint8) { - val := iter.readUint32(iter.nextToken()) - if val > math.MaxUint8 { - iter.ReportError("ReadUint8", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return uint8(val) -} - -// ReadInt16 read int16 -func (iter *Iterator) ReadInt16() (ret int16) { - c := iter.nextToken() - if c == '-' { - val := iter.readUint32(iter.readByte()) - if val > math.MaxInt16+1 { - iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return -int16(val) - } - val := iter.readUint32(c) - if val > math.MaxInt16 { - iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return int16(val) -} - -// ReadUint16 read uint16 -func (iter *Iterator) ReadUint16() (ret uint16) { - val := iter.readUint32(iter.nextToken()) - if val > math.MaxUint16 { - iter.ReportError("ReadUint16", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return uint16(val) -} - -// ReadInt32 read int32 -func (iter *Iterator) ReadInt32() (ret int32) { - c := iter.nextToken() - if c == '-' { - val := iter.readUint32(iter.readByte()) - if val > math.MaxInt32+1 { - iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return -int32(val) - } - val := iter.readUint32(c) - if val > math.MaxInt32 { - iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10)) - return - } - return int32(val) -} - -// ReadUint32 read uint32 -func (iter *Iterator) ReadUint32() (ret uint32) { - return iter.readUint32(iter.nextToken()) -} - -func (iter *Iterator) readUint32(c byte) (ret uint32) { - ind := intDigits[c] - if ind == 0 { - iter.assertInteger() - return 0 // single zero - } - if ind == invalidCharForNumber { - iter.ReportError("readUint32", "unexpected character: "+string([]byte{byte(ind)})) - return - } - value := uint32(ind) - if iter.tail-iter.head > 10 { - i := iter.head - ind2 := intDigits[iter.buf[i]] - if ind2 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value - } - i++ - ind3 := intDigits[iter.buf[i]] - if ind3 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*10 + uint32(ind2) - } - //iter.head = i + 1 - //value = value * 100 + uint32(ind2) * 10 + uint32(ind3) - i++ - ind4 := intDigits[iter.buf[i]] - if ind4 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*100 + uint32(ind2)*10 + uint32(ind3) - } - i++ - ind5 := intDigits[iter.buf[i]] - if ind5 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4) - } - i++ - ind6 := intDigits[iter.buf[i]] - if ind6 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5) - } - i++ - ind7 := intDigits[iter.buf[i]] - if ind7 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6) - } - i++ - ind8 := intDigits[iter.buf[i]] - if ind8 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7) - } - i++ - ind9 := intDigits[iter.buf[i]] - value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8) - iter.head = i - if ind9 == invalidCharForNumber { - iter.assertInteger() - return value - } - } - for { - for i := iter.head; i < iter.tail; i++ { - ind = intDigits[iter.buf[i]] - if ind == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value - } - if value > uint32SafeToMultiply10 { - value2 := (value << 3) + (value << 1) + uint32(ind) - if value2 < value { - iter.ReportError("readUint32", "overflow") - return - } - value = value2 - continue - } - value = (value << 3) + (value << 1) + uint32(ind) - } - if !iter.loadMore() { - iter.assertInteger() - return value - } - } -} - -// ReadInt64 read int64 -func (iter *Iterator) ReadInt64() (ret int64) { - c := iter.nextToken() - if c == '-' { - val := iter.readUint64(iter.readByte()) - if val > math.MaxInt64+1 { - iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10)) - return - } - return -int64(val) - } - val := iter.readUint64(c) - if val > math.MaxInt64 { - iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10)) - return - } - return int64(val) -} - -// ReadUint64 read uint64 -func (iter *Iterator) ReadUint64() uint64 { - return iter.readUint64(iter.nextToken()) -} - -func (iter *Iterator) readUint64(c byte) (ret uint64) { - ind := intDigits[c] - if ind == 0 { - iter.assertInteger() - return 0 // single zero - } - if ind == invalidCharForNumber { - iter.ReportError("readUint64", "unexpected character: "+string([]byte{byte(ind)})) - return - } - value := uint64(ind) - if iter.tail-iter.head > 10 { - i := iter.head - ind2 := intDigits[iter.buf[i]] - if ind2 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value - } - i++ - ind3 := intDigits[iter.buf[i]] - if ind3 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*10 + uint64(ind2) - } - //iter.head = i + 1 - //value = value * 100 + uint32(ind2) * 10 + uint32(ind3) - i++ - ind4 := intDigits[iter.buf[i]] - if ind4 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*100 + uint64(ind2)*10 + uint64(ind3) - } - i++ - ind5 := intDigits[iter.buf[i]] - if ind5 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4) - } - i++ - ind6 := intDigits[iter.buf[i]] - if ind6 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5) - } - i++ - ind7 := intDigits[iter.buf[i]] - if ind7 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6) - } - i++ - ind8 := intDigits[iter.buf[i]] - if ind8 == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7) - } - i++ - ind9 := intDigits[iter.buf[i]] - value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8) - iter.head = i - if ind9 == invalidCharForNumber { - iter.assertInteger() - return value - } - } - for { - for i := iter.head; i < iter.tail; i++ { - ind = intDigits[iter.buf[i]] - if ind == invalidCharForNumber { - iter.head = i - iter.assertInteger() - return value - } - if value > uint64SafeToMultiple10 { - value2 := (value << 3) + (value << 1) + uint64(ind) - if value2 < value { - iter.ReportError("readUint64", "overflow") - return - } - value = value2 - continue - } - value = (value << 3) + (value << 1) + uint64(ind) - } - if !iter.loadMore() { - iter.assertInteger() - return value - } - } -} - -func (iter *Iterator) assertInteger() { - if iter.head < iter.tail && iter.buf[iter.head] == '.' { - iter.ReportError("assertInteger", "can not decode float as int") - } -} diff --git a/vendor/github.com/json-iterator/go/iter_object.go b/vendor/github.com/json-iterator/go/iter_object.go deleted file mode 100644 index 58ee89c8..00000000 --- a/vendor/github.com/json-iterator/go/iter_object.go +++ /dev/null @@ -1,267 +0,0 @@ -package jsoniter - -import ( - "fmt" - "strings" -) - -// ReadObject read one field from object. -// If object ended, returns empty string. -// Otherwise, returns the field name. -func (iter *Iterator) ReadObject() (ret string) { - c := iter.nextToken() - switch c { - case 'n': - iter.skipThreeBytes('u', 'l', 'l') - return "" // null - case '{': - c = iter.nextToken() - if c == '"' { - iter.unreadByte() - field := iter.ReadString() - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - return field - } - if c == '}' { - return "" // end of object - } - iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c})) - return - case ',': - field := iter.ReadString() - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - return field - case '}': - return "" // end of object - default: - iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c}))) - return - } -} - -// CaseInsensitive -func (iter *Iterator) readFieldHash() int64 { - hash := int64(0x811c9dc5) - c := iter.nextToken() - if c != '"' { - iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c})) - return 0 - } - for { - for i := iter.head; i < iter.tail; i++ { - // require ascii string and no escape - b := iter.buf[i] - if b == '\\' { - iter.head = i - for _, b := range iter.readStringSlowPath() { - if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive { - b += 'a' - 'A' - } - hash ^= int64(b) - hash *= 0x1000193 - } - c = iter.nextToken() - if c != ':' { - iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) - return 0 - } - return hash - } - if b == '"' { - iter.head = i + 1 - c = iter.nextToken() - if c != ':' { - iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) - return 0 - } - return hash - } - if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive { - b += 'a' - 'A' - } - hash ^= int64(b) - hash *= 0x1000193 - } - if !iter.loadMore() { - iter.ReportError("readFieldHash", `incomplete field name`) - return 0 - } - } -} - -func calcHash(str string, caseSensitive bool) int64 { - if !caseSensitive { - str = strings.ToLower(str) - } - hash := int64(0x811c9dc5) - for _, b := range []byte(str) { - hash ^= int64(b) - hash *= 0x1000193 - } - return int64(hash) -} - -// ReadObjectCB read object with callback, the key is ascii only and field name not copied -func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { - c := iter.nextToken() - var field string - if c == '{' { - if !iter.incrementDepth() { - return false - } - c = iter.nextToken() - if c == '"' { - iter.unreadByte() - field = iter.ReadString() - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - if !callback(iter, field) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - for c == ',' { - field = iter.ReadString() - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - if !callback(iter, field) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - } - if c != '}' { - iter.ReportError("ReadObjectCB", `object not ended with }`) - iter.decrementDepth() - return false - } - return iter.decrementDepth() - } - if c == '}' { - return iter.decrementDepth() - } - iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c})) - iter.decrementDepth() - return false - } - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return true // null - } - iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c})) - return false -} - -// ReadMapCB read map with callback, the key can be any string -func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { - c := iter.nextToken() - if c == '{' { - if !iter.incrementDepth() { - return false - } - c = iter.nextToken() - if c == '"' { - iter.unreadByte() - field := iter.ReadString() - if iter.nextToken() != ':' { - iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) - iter.decrementDepth() - return false - } - if !callback(iter, field) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - for c == ',' { - field = iter.ReadString() - if iter.nextToken() != ':' { - iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) - iter.decrementDepth() - return false - } - if !callback(iter, field) { - iter.decrementDepth() - return false - } - c = iter.nextToken() - } - if c != '}' { - iter.ReportError("ReadMapCB", `object not ended with }`) - iter.decrementDepth() - return false - } - return iter.decrementDepth() - } - if c == '}' { - return iter.decrementDepth() - } - iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c})) - iter.decrementDepth() - return false - } - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return true // null - } - iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c})) - return false -} - -func (iter *Iterator) readObjectStart() bool { - c := iter.nextToken() - if c == '{' { - c = iter.nextToken() - if c == '}' { - return false - } - iter.unreadByte() - return true - } else if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return false - } - iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c})) - return false -} - -func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) { - str := iter.ReadStringAsSlice() - if iter.skipWhitespacesWithoutLoadMore() { - if ret == nil { - ret = make([]byte, len(str)) - copy(ret, str) - } - if !iter.loadMore() { - return - } - } - if iter.buf[iter.head] != ':' { - iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]})) - return - } - iter.head++ - if iter.skipWhitespacesWithoutLoadMore() { - if ret == nil { - ret = make([]byte, len(str)) - copy(ret, str) - } - if !iter.loadMore() { - return - } - } - if ret == nil { - return str - } - return ret -} diff --git a/vendor/github.com/json-iterator/go/iter_skip.go b/vendor/github.com/json-iterator/go/iter_skip.go deleted file mode 100644 index e91eefb1..00000000 --- a/vendor/github.com/json-iterator/go/iter_skip.go +++ /dev/null @@ -1,130 +0,0 @@ -package jsoniter - -import "fmt" - -// ReadNil reads a json object as nil and -// returns whether it's a nil or not -func (iter *Iterator) ReadNil() (ret bool) { - c := iter.nextToken() - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') // null - return true - } - iter.unreadByte() - return false -} - -// ReadBool reads a json object as BoolValue -func (iter *Iterator) ReadBool() (ret bool) { - c := iter.nextToken() - if c == 't' { - iter.skipThreeBytes('r', 'u', 'e') - return true - } - if c == 'f' { - iter.skipFourBytes('a', 'l', 's', 'e') - return false - } - iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c})) - return -} - -// SkipAndReturnBytes skip next JSON element, and return its content as []byte. -// The []byte can be kept, it is a copy of data. -func (iter *Iterator) SkipAndReturnBytes() []byte { - iter.startCapture(iter.head) - iter.Skip() - return iter.stopCapture() -} - -// SkipAndAppendBytes skips next JSON element and appends its content to -// buffer, returning the result. -func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte { - iter.startCaptureTo(buf, iter.head) - iter.Skip() - return iter.stopCapture() -} - -func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) { - if iter.captured != nil { - panic("already in capture mode") - } - iter.captureStartedAt = captureStartedAt - iter.captured = buf -} - -func (iter *Iterator) startCapture(captureStartedAt int) { - iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt) -} - -func (iter *Iterator) stopCapture() []byte { - if iter.captured == nil { - panic("not in capture mode") - } - captured := iter.captured - remaining := iter.buf[iter.captureStartedAt:iter.head] - iter.captureStartedAt = -1 - iter.captured = nil - return append(captured, remaining...) -} - -// Skip skips a json object and positions to relatively the next json object -func (iter *Iterator) Skip() { - c := iter.nextToken() - switch c { - case '"': - iter.skipString() - case 'n': - iter.skipThreeBytes('u', 'l', 'l') // null - case 't': - iter.skipThreeBytes('r', 'u', 'e') // true - case 'f': - iter.skipFourBytes('a', 'l', 's', 'e') // false - case '0': - iter.unreadByte() - iter.ReadFloat32() - case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9': - iter.skipNumber() - case '[': - iter.skipArray() - case '{': - iter.skipObject() - default: - iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c)) - return - } -} - -func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) { - if iter.readByte() != b1 { - iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) - return - } - if iter.readByte() != b2 { - iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) - return - } - if iter.readByte() != b3 { - iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) - return - } - if iter.readByte() != b4 { - iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) - return - } -} - -func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) { - if iter.readByte() != b1 { - iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) - return - } - if iter.readByte() != b2 { - iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) - return - } - if iter.readByte() != b3 { - iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) - return - } -} diff --git a/vendor/github.com/json-iterator/go/iter_skip_sloppy.go b/vendor/github.com/json-iterator/go/iter_skip_sloppy.go deleted file mode 100644 index 9303de41..00000000 --- a/vendor/github.com/json-iterator/go/iter_skip_sloppy.go +++ /dev/null @@ -1,163 +0,0 @@ -//+build jsoniter_sloppy - -package jsoniter - -// sloppy but faster implementation, do not validate the input json - -func (iter *Iterator) skipNumber() { - for { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - switch c { - case ' ', '\n', '\r', '\t', ',', '}', ']': - iter.head = i - return - } - } - if !iter.loadMore() { - return - } - } -} - -func (iter *Iterator) skipArray() { - level := 1 - if !iter.incrementDepth() { - return - } - for { - for i := iter.head; i < iter.tail; i++ { - switch iter.buf[i] { - case '"': // If inside string, skip it - iter.head = i + 1 - iter.skipString() - i = iter.head - 1 // it will be i++ soon - case '[': // If open symbol, increase level - level++ - if !iter.incrementDepth() { - return - } - case ']': // If close symbol, increase level - level-- - if !iter.decrementDepth() { - return - } - - // If we have returned to the original level, we're done - if level == 0 { - iter.head = i + 1 - return - } - } - } - if !iter.loadMore() { - iter.ReportError("skipObject", "incomplete array") - return - } - } -} - -func (iter *Iterator) skipObject() { - level := 1 - if !iter.incrementDepth() { - return - } - - for { - for i := iter.head; i < iter.tail; i++ { - switch iter.buf[i] { - case '"': // If inside string, skip it - iter.head = i + 1 - iter.skipString() - i = iter.head - 1 // it will be i++ soon - case '{': // If open symbol, increase level - level++ - if !iter.incrementDepth() { - return - } - case '}': // If close symbol, increase level - level-- - if !iter.decrementDepth() { - return - } - - // If we have returned to the original level, we're done - if level == 0 { - iter.head = i + 1 - return - } - } - } - if !iter.loadMore() { - iter.ReportError("skipObject", "incomplete object") - return - } - } -} - -func (iter *Iterator) skipString() { - for { - end, escaped := iter.findStringEnd() - if end == -1 { - if !iter.loadMore() { - iter.ReportError("skipString", "incomplete string") - return - } - if escaped { - iter.head = 1 // skip the first char as last char read is \ - } - } else { - iter.head = end - return - } - } -} - -// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go -// Tries to find the end of string -// Support if string contains escaped quote symbols. -func (iter *Iterator) findStringEnd() (int, bool) { - escaped := false - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - if c == '"' { - if !escaped { - return i + 1, false - } - j := i - 1 - for { - if j < iter.head || iter.buf[j] != '\\' { - // even number of backslashes - // either end of buffer, or " found - return i + 1, true - } - j-- - if j < iter.head || iter.buf[j] != '\\' { - // odd number of backslashes - // it is \" or \\\" - break - } - j-- - } - } else if c == '\\' { - escaped = true - } - } - j := iter.tail - 1 - for { - if j < iter.head || iter.buf[j] != '\\' { - // even number of backslashes - // either end of buffer, or " found - return -1, false // do not end with \ - } - j-- - if j < iter.head || iter.buf[j] != '\\' { - // odd number of backslashes - // it is \" or \\\" - break - } - j-- - - } - return -1, true // end with \ -} diff --git a/vendor/github.com/json-iterator/go/iter_skip_strict.go b/vendor/github.com/json-iterator/go/iter_skip_strict.go deleted file mode 100644 index 6cf66d04..00000000 --- a/vendor/github.com/json-iterator/go/iter_skip_strict.go +++ /dev/null @@ -1,99 +0,0 @@ -//+build !jsoniter_sloppy - -package jsoniter - -import ( - "fmt" - "io" -) - -func (iter *Iterator) skipNumber() { - if !iter.trySkipNumber() { - iter.unreadByte() - if iter.Error != nil && iter.Error != io.EOF { - return - } - iter.ReadFloat64() - if iter.Error != nil && iter.Error != io.EOF { - iter.Error = nil - iter.ReadBigFloat() - } - } -} - -func (iter *Iterator) trySkipNumber() bool { - dotFound := false - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - case '.': - if dotFound { - iter.ReportError("validateNumber", `more than one dot found in number`) - return true // already failed - } - if i+1 == iter.tail { - return false - } - c = iter.buf[i+1] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - default: - iter.ReportError("validateNumber", `missing digit after dot`) - return true // already failed - } - dotFound = true - default: - switch c { - case ',', ']', '}', ' ', '\t', '\n', '\r': - if iter.head == i { - return false // if - without following digits - } - iter.head = i - return true // must be valid - } - return false // may be invalid - } - } - return false -} - -func (iter *Iterator) skipString() { - if !iter.trySkipString() { - iter.unreadByte() - iter.ReadString() - } -} - -func (iter *Iterator) trySkipString() bool { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - if c == '"' { - iter.head = i + 1 - return true // valid - } else if c == '\\' { - return false - } else if c < ' ' { - iter.ReportError("trySkipString", - fmt.Sprintf(`invalid control character found: %d`, c)) - return true // already failed - } - } - return false -} - -func (iter *Iterator) skipObject() { - iter.unreadByte() - iter.ReadObjectCB(func(iter *Iterator, field string) bool { - iter.Skip() - return true - }) -} - -func (iter *Iterator) skipArray() { - iter.unreadByte() - iter.ReadArrayCB(func(iter *Iterator) bool { - iter.Skip() - return true - }) -} diff --git a/vendor/github.com/json-iterator/go/iter_str.go b/vendor/github.com/json-iterator/go/iter_str.go deleted file mode 100644 index adc487ea..00000000 --- a/vendor/github.com/json-iterator/go/iter_str.go +++ /dev/null @@ -1,215 +0,0 @@ -package jsoniter - -import ( - "fmt" - "unicode/utf16" -) - -// ReadString read string from iterator -func (iter *Iterator) ReadString() (ret string) { - c := iter.nextToken() - if c == '"' { - for i := iter.head; i < iter.tail; i++ { - c := iter.buf[i] - if c == '"' { - ret = string(iter.buf[iter.head:i]) - iter.head = i + 1 - return ret - } else if c == '\\' { - break - } else if c < ' ' { - iter.ReportError("ReadString", - fmt.Sprintf(`invalid control character found: %d`, c)) - return - } - } - return iter.readStringSlowPath() - } else if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return "" - } - iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c})) - return -} - -func (iter *Iterator) readStringSlowPath() (ret string) { - var str []byte - var c byte - for iter.Error == nil { - c = iter.readByte() - if c == '"' { - return string(str) - } - if c == '\\' { - c = iter.readByte() - str = iter.readEscapedChar(c, str) - } else { - str = append(str, c) - } - } - iter.ReportError("readStringSlowPath", "unexpected end of input") - return -} - -func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte { - switch c { - case 'u': - r := iter.readU4() - if utf16.IsSurrogate(r) { - c = iter.readByte() - if iter.Error != nil { - return nil - } - if c != '\\' { - iter.unreadByte() - str = appendRune(str, r) - return str - } - c = iter.readByte() - if iter.Error != nil { - return nil - } - if c != 'u' { - str = appendRune(str, r) - return iter.readEscapedChar(c, str) - } - r2 := iter.readU4() - if iter.Error != nil { - return nil - } - combined := utf16.DecodeRune(r, r2) - if combined == '\uFFFD' { - str = appendRune(str, r) - str = appendRune(str, r2) - } else { - str = appendRune(str, combined) - } - } else { - str = appendRune(str, r) - } - case '"': - str = append(str, '"') - case '\\': - str = append(str, '\\') - case '/': - str = append(str, '/') - case 'b': - str = append(str, '\b') - case 'f': - str = append(str, '\f') - case 'n': - str = append(str, '\n') - case 'r': - str = append(str, '\r') - case 't': - str = append(str, '\t') - default: - iter.ReportError("readEscapedChar", - `invalid escape char after \`) - return nil - } - return str -} - -// ReadStringAsSlice read string from iterator without copying into string form. -// The []byte can not be kept, as it will change after next iterator call. -func (iter *Iterator) ReadStringAsSlice() (ret []byte) { - c := iter.nextToken() - if c == '"' { - for i := iter.head; i < iter.tail; i++ { - // require ascii string and no escape - // for: field name, base64, number - if iter.buf[i] == '"' { - // fast path: reuse the underlying buffer - ret = iter.buf[iter.head:i] - iter.head = i + 1 - return ret - } - } - readLen := iter.tail - iter.head - copied := make([]byte, readLen, readLen*2) - copy(copied, iter.buf[iter.head:iter.tail]) - iter.head = iter.tail - for iter.Error == nil { - c := iter.readByte() - if c == '"' { - return copied - } - copied = append(copied, c) - } - return copied - } - iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c})) - return -} - -func (iter *Iterator) readU4() (ret rune) { - for i := 0; i < 4; i++ { - c := iter.readByte() - if iter.Error != nil { - return - } - if c >= '0' && c <= '9' { - ret = ret*16 + rune(c-'0') - } else if c >= 'a' && c <= 'f' { - ret = ret*16 + rune(c-'a'+10) - } else if c >= 'A' && c <= 'F' { - ret = ret*16 + rune(c-'A'+10) - } else { - iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c})) - return - } - } - return ret -} - -const ( - t1 = 0x00 // 0000 0000 - tx = 0x80 // 1000 0000 - t2 = 0xC0 // 1100 0000 - t3 = 0xE0 // 1110 0000 - t4 = 0xF0 // 1111 0000 - t5 = 0xF8 // 1111 1000 - - maskx = 0x3F // 0011 1111 - mask2 = 0x1F // 0001 1111 - mask3 = 0x0F // 0000 1111 - mask4 = 0x07 // 0000 0111 - - rune1Max = 1<<7 - 1 - rune2Max = 1<<11 - 1 - rune3Max = 1<<16 - 1 - - surrogateMin = 0xD800 - surrogateMax = 0xDFFF - - maxRune = '\U0010FFFF' // Maximum valid Unicode code point. - runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character" -) - -func appendRune(p []byte, r rune) []byte { - // Negative values are erroneous. Making it unsigned addresses the problem. - switch i := uint32(r); { - case i <= rune1Max: - p = append(p, byte(r)) - return p - case i <= rune2Max: - p = append(p, t2|byte(r>>6)) - p = append(p, tx|byte(r)&maskx) - return p - case i > maxRune, surrogateMin <= i && i <= surrogateMax: - r = runeError - fallthrough - case i <= rune3Max: - p = append(p, t3|byte(r>>12)) - p = append(p, tx|byte(r>>6)&maskx) - p = append(p, tx|byte(r)&maskx) - return p - default: - p = append(p, t4|byte(r>>18)) - p = append(p, tx|byte(r>>12)&maskx) - p = append(p, tx|byte(r>>6)&maskx) - p = append(p, tx|byte(r)&maskx) - return p - } -} diff --git a/vendor/github.com/json-iterator/go/jsoniter.go b/vendor/github.com/json-iterator/go/jsoniter.go deleted file mode 100644 index c2934f91..00000000 --- a/vendor/github.com/json-iterator/go/jsoniter.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package jsoniter implements encoding and decoding of JSON as defined in -// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json. -// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter -// and variable type declarations (if any). -// jsoniter interfaces gives 100% compatibility with code using standard lib. -// -// "JSON and Go" -// (https://golang.org/doc/articles/json_and_go.html) -// gives a description of how Marshal/Unmarshal operate -// between arbitrary or predefined json objects and bytes, -// and it applies to jsoniter.Marshal/Unmarshal as well. -// -// Besides, jsoniter.Iterator provides a different set of interfaces -// iterating given bytes/string/reader -// and yielding parsed elements one by one. -// This set of interfaces reads input as required and gives -// better performance. -package jsoniter diff --git a/vendor/github.com/json-iterator/go/pool.go b/vendor/github.com/json-iterator/go/pool.go deleted file mode 100644 index e2389b56..00000000 --- a/vendor/github.com/json-iterator/go/pool.go +++ /dev/null @@ -1,42 +0,0 @@ -package jsoniter - -import ( - "io" -) - -// IteratorPool a thread safe pool of iterators with same configuration -type IteratorPool interface { - BorrowIterator(data []byte) *Iterator - ReturnIterator(iter *Iterator) -} - -// StreamPool a thread safe pool of streams with same configuration -type StreamPool interface { - BorrowStream(writer io.Writer) *Stream - ReturnStream(stream *Stream) -} - -func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream { - stream := cfg.streamPool.Get().(*Stream) - stream.Reset(writer) - return stream -} - -func (cfg *frozenConfig) ReturnStream(stream *Stream) { - stream.out = nil - stream.Error = nil - stream.Attachment = nil - cfg.streamPool.Put(stream) -} - -func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator { - iter := cfg.iteratorPool.Get().(*Iterator) - iter.ResetBytes(data) - return iter -} - -func (cfg *frozenConfig) ReturnIterator(iter *Iterator) { - iter.Error = nil - iter.Attachment = nil - cfg.iteratorPool.Put(iter) -} diff --git a/vendor/github.com/json-iterator/go/reflect.go b/vendor/github.com/json-iterator/go/reflect.go deleted file mode 100644 index 39acb320..00000000 --- a/vendor/github.com/json-iterator/go/reflect.go +++ /dev/null @@ -1,337 +0,0 @@ -package jsoniter - -import ( - "fmt" - "reflect" - "unsafe" - - "github.com/modern-go/reflect2" -) - -// ValDecoder is an internal type registered to cache as needed. -// Don't confuse jsoniter.ValDecoder with json.Decoder. -// For json.Decoder's adapter, refer to jsoniter.AdapterDecoder(todo link). -// -// Reflection on type to create decoders, which is then cached -// Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions -// 1. create instance of new value, for example *int will need a int to be allocated -// 2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New -// 3. assignment to map, both key and value will be reflect.Value -// For a simple struct binding, it will be reflect.Value free and allocation free -type ValDecoder interface { - Decode(ptr unsafe.Pointer, iter *Iterator) -} - -// ValEncoder is an internal type registered to cache as needed. -// Don't confuse jsoniter.ValEncoder with json.Encoder. -// For json.Encoder's adapter, refer to jsoniter.AdapterEncoder(todo godoc link). -type ValEncoder interface { - IsEmpty(ptr unsafe.Pointer) bool - Encode(ptr unsafe.Pointer, stream *Stream) -} - -type checkIsEmpty interface { - IsEmpty(ptr unsafe.Pointer) bool -} - -type ctx struct { - *frozenConfig - prefix string - encoders map[reflect2.Type]ValEncoder - decoders map[reflect2.Type]ValDecoder -} - -func (b *ctx) caseSensitive() bool { - if b.frozenConfig == nil { - // default is case-insensitive - return false - } - return b.frozenConfig.caseSensitive -} - -func (b *ctx) append(prefix string) *ctx { - return &ctx{ - frozenConfig: b.frozenConfig, - prefix: b.prefix + " " + prefix, - encoders: b.encoders, - decoders: b.decoders, - } -} - -// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal -func (iter *Iterator) ReadVal(obj interface{}) { - depth := iter.depth - cacheKey := reflect2.RTypeOf(obj) - decoder := iter.cfg.getDecoderFromCache(cacheKey) - if decoder == nil { - typ := reflect2.TypeOf(obj) - if typ == nil || typ.Kind() != reflect.Ptr { - iter.ReportError("ReadVal", "can only unmarshal into pointer") - return - } - decoder = iter.cfg.DecoderOf(typ) - } - ptr := reflect2.PtrOf(obj) - if ptr == nil { - iter.ReportError("ReadVal", "can not read into nil pointer") - return - } - decoder.Decode(ptr, iter) - if iter.depth != depth { - iter.ReportError("ReadVal", "unexpected mismatched nesting") - return - } -} - -// WriteVal copy the go interface into underlying JSON, same as json.Marshal -func (stream *Stream) WriteVal(val interface{}) { - if nil == val { - stream.WriteNil() - return - } - cacheKey := reflect2.RTypeOf(val) - encoder := stream.cfg.getEncoderFromCache(cacheKey) - if encoder == nil { - typ := reflect2.TypeOf(val) - encoder = stream.cfg.EncoderOf(typ) - } - encoder.Encode(reflect2.PtrOf(val), stream) -} - -func (cfg *frozenConfig) DecoderOf(typ reflect2.Type) ValDecoder { - cacheKey := typ.RType() - decoder := cfg.getDecoderFromCache(cacheKey) - if decoder != nil { - return decoder - } - ctx := &ctx{ - frozenConfig: cfg, - prefix: "", - decoders: map[reflect2.Type]ValDecoder{}, - encoders: map[reflect2.Type]ValEncoder{}, - } - ptrType := typ.(*reflect2.UnsafePtrType) - decoder = decoderOfType(ctx, ptrType.Elem()) - cfg.addDecoderToCache(cacheKey, decoder) - return decoder -} - -func decoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { - decoder := getTypeDecoderFromExtension(ctx, typ) - if decoder != nil { - return decoder - } - decoder = createDecoderOfType(ctx, typ) - for _, extension := range extensions { - decoder = extension.DecorateDecoder(typ, decoder) - } - decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder) - for _, extension := range ctx.extraExtensions { - decoder = extension.DecorateDecoder(typ, decoder) - } - return decoder -} - -func createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { - decoder := ctx.decoders[typ] - if decoder != nil { - return decoder - } - placeholder := &placeholderDecoder{} - ctx.decoders[typ] = placeholder - decoder = _createDecoderOfType(ctx, typ) - placeholder.decoder = decoder - return decoder -} - -func _createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { - decoder := createDecoderOfJsonRawMessage(ctx, typ) - if decoder != nil { - return decoder - } - decoder = createDecoderOfJsonNumber(ctx, typ) - if decoder != nil { - return decoder - } - decoder = createDecoderOfMarshaler(ctx, typ) - if decoder != nil { - return decoder - } - decoder = createDecoderOfAny(ctx, typ) - if decoder != nil { - return decoder - } - decoder = createDecoderOfNative(ctx, typ) - if decoder != nil { - return decoder - } - switch typ.Kind() { - case reflect.Interface: - ifaceType, isIFace := typ.(*reflect2.UnsafeIFaceType) - if isIFace { - return &ifaceDecoder{valType: ifaceType} - } - return &efaceDecoder{} - case reflect.Struct: - return decoderOfStruct(ctx, typ) - case reflect.Array: - return decoderOfArray(ctx, typ) - case reflect.Slice: - return decoderOfSlice(ctx, typ) - case reflect.Map: - return decoderOfMap(ctx, typ) - case reflect.Ptr: - return decoderOfOptional(ctx, typ) - default: - return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} - } -} - -func (cfg *frozenConfig) EncoderOf(typ reflect2.Type) ValEncoder { - cacheKey := typ.RType() - encoder := cfg.getEncoderFromCache(cacheKey) - if encoder != nil { - return encoder - } - ctx := &ctx{ - frozenConfig: cfg, - prefix: "", - decoders: map[reflect2.Type]ValDecoder{}, - encoders: map[reflect2.Type]ValEncoder{}, - } - encoder = encoderOfType(ctx, typ) - if typ.LikePtr() { - encoder = &onePtrEncoder{encoder} - } - cfg.addEncoderToCache(cacheKey, encoder) - return encoder -} - -type onePtrEncoder struct { - encoder ValEncoder -} - -func (encoder *onePtrEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr)) -} - -func (encoder *onePtrEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - encoder.encoder.Encode(unsafe.Pointer(&ptr), stream) -} - -func encoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { - encoder := getTypeEncoderFromExtension(ctx, typ) - if encoder != nil { - return encoder - } - encoder = createEncoderOfType(ctx, typ) - for _, extension := range extensions { - encoder = extension.DecorateEncoder(typ, encoder) - } - encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder) - for _, extension := range ctx.extraExtensions { - encoder = extension.DecorateEncoder(typ, encoder) - } - return encoder -} - -func createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { - encoder := ctx.encoders[typ] - if encoder != nil { - return encoder - } - placeholder := &placeholderEncoder{} - ctx.encoders[typ] = placeholder - encoder = _createEncoderOfType(ctx, typ) - placeholder.encoder = encoder - return encoder -} -func _createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { - encoder := createEncoderOfJsonRawMessage(ctx, typ) - if encoder != nil { - return encoder - } - encoder = createEncoderOfJsonNumber(ctx, typ) - if encoder != nil { - return encoder - } - encoder = createEncoderOfMarshaler(ctx, typ) - if encoder != nil { - return encoder - } - encoder = createEncoderOfAny(ctx, typ) - if encoder != nil { - return encoder - } - encoder = createEncoderOfNative(ctx, typ) - if encoder != nil { - return encoder - } - kind := typ.Kind() - switch kind { - case reflect.Interface: - return &dynamicEncoder{typ} - case reflect.Struct: - return encoderOfStruct(ctx, typ) - case reflect.Array: - return encoderOfArray(ctx, typ) - case reflect.Slice: - return encoderOfSlice(ctx, typ) - case reflect.Map: - return encoderOfMap(ctx, typ) - case reflect.Ptr: - return encoderOfOptional(ctx, typ) - default: - return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} - } -} - -type lazyErrorDecoder struct { - err error -} - -func (decoder *lazyErrorDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.WhatIsNext() != NilValue { - if iter.Error == nil { - iter.Error = decoder.err - } - } else { - iter.Skip() - } -} - -type lazyErrorEncoder struct { - err error -} - -func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if ptr == nil { - stream.WriteNil() - } else if stream.Error == nil { - stream.Error = encoder.err - } -} - -func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return false -} - -type placeholderDecoder struct { - decoder ValDecoder -} - -func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.decoder.Decode(ptr, iter) -} - -type placeholderEncoder struct { - encoder ValEncoder -} - -func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - encoder.encoder.Encode(ptr, stream) -} - -func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.encoder.IsEmpty(ptr) -} diff --git a/vendor/github.com/json-iterator/go/reflect_array.go b/vendor/github.com/json-iterator/go/reflect_array.go deleted file mode 100644 index 13a0b7b0..00000000 --- a/vendor/github.com/json-iterator/go/reflect_array.go +++ /dev/null @@ -1,104 +0,0 @@ -package jsoniter - -import ( - "fmt" - "github.com/modern-go/reflect2" - "io" - "unsafe" -) - -func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder { - arrayType := typ.(*reflect2.UnsafeArrayType) - decoder := decoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) - return &arrayDecoder{arrayType, decoder} -} - -func encoderOfArray(ctx *ctx, typ reflect2.Type) ValEncoder { - arrayType := typ.(*reflect2.UnsafeArrayType) - if arrayType.Len() == 0 { - return emptyArrayEncoder{} - } - encoder := encoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) - return &arrayEncoder{arrayType, encoder} -} - -type emptyArrayEncoder struct{} - -func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteEmptyArray() -} - -func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return true -} - -type arrayEncoder struct { - arrayType *reflect2.UnsafeArrayType - elemEncoder ValEncoder -} - -func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteArrayStart() - elemPtr := unsafe.Pointer(ptr) - encoder.elemEncoder.Encode(elemPtr, stream) - for i := 1; i < encoder.arrayType.Len(); i++ { - stream.WriteMore() - elemPtr = encoder.arrayType.UnsafeGetIndex(ptr, i) - encoder.elemEncoder.Encode(elemPtr, stream) - } - stream.WriteArrayEnd() - if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error()) - } -} - -func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return false -} - -type arrayDecoder struct { - arrayType *reflect2.UnsafeArrayType - elemDecoder ValDecoder -} - -func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.doDecode(ptr, iter) - if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error()) - } -} - -func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { - c := iter.nextToken() - arrayType := decoder.arrayType - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - return - } - if c != '[' { - iter.ReportError("decode array", "expect [ or n, but found "+string([]byte{c})) - return - } - c = iter.nextToken() - if c == ']' { - return - } - iter.unreadByte() - elemPtr := arrayType.UnsafeGetIndex(ptr, 0) - decoder.elemDecoder.Decode(elemPtr, iter) - length := 1 - for c = iter.nextToken(); c == ','; c = iter.nextToken() { - if length >= arrayType.Len() { - iter.Skip() - continue - } - idx := length - length += 1 - elemPtr = arrayType.UnsafeGetIndex(ptr, idx) - decoder.elemDecoder.Decode(elemPtr, iter) - } - if c != ']' { - iter.ReportError("decode array", "expect ], but found "+string([]byte{c})) - return - } -} diff --git a/vendor/github.com/json-iterator/go/reflect_dynamic.go b/vendor/github.com/json-iterator/go/reflect_dynamic.go deleted file mode 100644 index 8b6bc8b4..00000000 --- a/vendor/github.com/json-iterator/go/reflect_dynamic.go +++ /dev/null @@ -1,70 +0,0 @@ -package jsoniter - -import ( - "github.com/modern-go/reflect2" - "reflect" - "unsafe" -) - -type dynamicEncoder struct { - valType reflect2.Type -} - -func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := encoder.valType.UnsafeIndirect(ptr) - stream.WriteVal(obj) -} - -func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.valType.UnsafeIndirect(ptr) == nil -} - -type efaceDecoder struct { -} - -func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - pObj := (*interface{})(ptr) - obj := *pObj - if obj == nil { - *pObj = iter.Read() - return - } - typ := reflect2.TypeOf(obj) - if typ.Kind() != reflect.Ptr { - *pObj = iter.Read() - return - } - ptrType := typ.(*reflect2.UnsafePtrType) - ptrElemType := ptrType.Elem() - if iter.WhatIsNext() == NilValue { - if ptrElemType.Kind() != reflect.Ptr { - iter.skipFourBytes('n', 'u', 'l', 'l') - *pObj = nil - return - } - } - if reflect2.IsNil(obj) { - obj := ptrElemType.New() - iter.ReadVal(obj) - *pObj = obj - return - } - iter.ReadVal(obj) -} - -type ifaceDecoder struct { - valType *reflect2.UnsafeIFaceType -} - -func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.ReadNil() { - decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew()) - return - } - obj := decoder.valType.UnsafeIndirect(ptr) - if reflect2.IsNil(obj) { - iter.ReportError("decode non empty interface", "can not unmarshal into nil") - return - } - iter.ReadVal(obj) -} diff --git a/vendor/github.com/json-iterator/go/reflect_extension.go b/vendor/github.com/json-iterator/go/reflect_extension.go deleted file mode 100644 index 74a97bfe..00000000 --- a/vendor/github.com/json-iterator/go/reflect_extension.go +++ /dev/null @@ -1,483 +0,0 @@ -package jsoniter - -import ( - "fmt" - "github.com/modern-go/reflect2" - "reflect" - "sort" - "strings" - "unicode" - "unsafe" -) - -var typeDecoders = map[string]ValDecoder{} -var fieldDecoders = map[string]ValDecoder{} -var typeEncoders = map[string]ValEncoder{} -var fieldEncoders = map[string]ValEncoder{} -var extensions = []Extension{} - -// StructDescriptor describe how should we encode/decode the struct -type StructDescriptor struct { - Type reflect2.Type - Fields []*Binding -} - -// GetField get one field from the descriptor by its name. -// Can not use map here to keep field orders. -func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding { - for _, binding := range structDescriptor.Fields { - if binding.Field.Name() == fieldName { - return binding - } - } - return nil -} - -// Binding describe how should we encode/decode the struct field -type Binding struct { - levels []int - Field reflect2.StructField - FromNames []string - ToNames []string - Encoder ValEncoder - Decoder ValDecoder -} - -// Extension the one for all SPI. Customize encoding/decoding by specifying alternate encoder/decoder. -// Can also rename fields by UpdateStructDescriptor. -type Extension interface { - UpdateStructDescriptor(structDescriptor *StructDescriptor) - CreateMapKeyDecoder(typ reflect2.Type) ValDecoder - CreateMapKeyEncoder(typ reflect2.Type) ValEncoder - CreateDecoder(typ reflect2.Type) ValDecoder - CreateEncoder(typ reflect2.Type) ValEncoder - DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder - DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder -} - -// DummyExtension embed this type get dummy implementation for all methods of Extension -type DummyExtension struct { -} - -// UpdateStructDescriptor No-op -func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { -} - -// CreateMapKeyDecoder No-op -func (extension *DummyExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { - return nil -} - -// CreateMapKeyEncoder No-op -func (extension *DummyExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { - return nil -} - -// CreateDecoder No-op -func (extension *DummyExtension) CreateDecoder(typ reflect2.Type) ValDecoder { - return nil -} - -// CreateEncoder No-op -func (extension *DummyExtension) CreateEncoder(typ reflect2.Type) ValEncoder { - return nil -} - -// DecorateDecoder No-op -func (extension *DummyExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { - return decoder -} - -// DecorateEncoder No-op -func (extension *DummyExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { - return encoder -} - -type EncoderExtension map[reflect2.Type]ValEncoder - -// UpdateStructDescriptor No-op -func (extension EncoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { -} - -// CreateDecoder No-op -func (extension EncoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder { - return nil -} - -// CreateEncoder get encoder from map -func (extension EncoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder { - return extension[typ] -} - -// CreateMapKeyDecoder No-op -func (extension EncoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { - return nil -} - -// CreateMapKeyEncoder No-op -func (extension EncoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { - return nil -} - -// DecorateDecoder No-op -func (extension EncoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { - return decoder -} - -// DecorateEncoder No-op -func (extension EncoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { - return encoder -} - -type DecoderExtension map[reflect2.Type]ValDecoder - -// UpdateStructDescriptor No-op -func (extension DecoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { -} - -// CreateMapKeyDecoder No-op -func (extension DecoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { - return nil -} - -// CreateMapKeyEncoder No-op -func (extension DecoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { - return nil -} - -// CreateDecoder get decoder from map -func (extension DecoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder { - return extension[typ] -} - -// CreateEncoder No-op -func (extension DecoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder { - return nil -} - -// DecorateDecoder No-op -func (extension DecoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { - return decoder -} - -// DecorateEncoder No-op -func (extension DecoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { - return encoder -} - -type funcDecoder struct { - fun DecoderFunc -} - -func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.fun(ptr, iter) -} - -type funcEncoder struct { - fun EncoderFunc - isEmptyFunc func(ptr unsafe.Pointer) bool -} - -func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - encoder.fun(ptr, stream) -} - -func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool { - if encoder.isEmptyFunc == nil { - return false - } - return encoder.isEmptyFunc(ptr) -} - -// DecoderFunc the function form of TypeDecoder -type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator) - -// EncoderFunc the function form of TypeEncoder -type EncoderFunc func(ptr unsafe.Pointer, stream *Stream) - -// RegisterTypeDecoderFunc register TypeDecoder for a type with function -func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) { - typeDecoders[typ] = &funcDecoder{fun} -} - -// RegisterTypeDecoder register TypeDecoder for a typ -func RegisterTypeDecoder(typ string, decoder ValDecoder) { - typeDecoders[typ] = decoder -} - -// RegisterFieldDecoderFunc register TypeDecoder for a struct field with function -func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) { - RegisterFieldDecoder(typ, field, &funcDecoder{fun}) -} - -// RegisterFieldDecoder register TypeDecoder for a struct field -func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) { - fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder -} - -// RegisterTypeEncoderFunc register TypeEncoder for a type with encode/isEmpty function -func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) { - typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc} -} - -// RegisterTypeEncoder register TypeEncoder for a type -func RegisterTypeEncoder(typ string, encoder ValEncoder) { - typeEncoders[typ] = encoder -} - -// RegisterFieldEncoderFunc register TypeEncoder for a struct field with encode/isEmpty function -func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) { - RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc}) -} - -// RegisterFieldEncoder register TypeEncoder for a struct field -func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) { - fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder -} - -// RegisterExtension register extension -func RegisterExtension(extension Extension) { - extensions = append(extensions, extension) -} - -func getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder { - decoder := _getTypeDecoderFromExtension(ctx, typ) - if decoder != nil { - for _, extension := range extensions { - decoder = extension.DecorateDecoder(typ, decoder) - } - decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder) - for _, extension := range ctx.extraExtensions { - decoder = extension.DecorateDecoder(typ, decoder) - } - } - return decoder -} -func _getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder { - for _, extension := range extensions { - decoder := extension.CreateDecoder(typ) - if decoder != nil { - return decoder - } - } - decoder := ctx.decoderExtension.CreateDecoder(typ) - if decoder != nil { - return decoder - } - for _, extension := range ctx.extraExtensions { - decoder := extension.CreateDecoder(typ) - if decoder != nil { - return decoder - } - } - typeName := typ.String() - decoder = typeDecoders[typeName] - if decoder != nil { - return decoder - } - if typ.Kind() == reflect.Ptr { - ptrType := typ.(*reflect2.UnsafePtrType) - decoder := typeDecoders[ptrType.Elem().String()] - if decoder != nil { - return &OptionalDecoder{ptrType.Elem(), decoder} - } - } - return nil -} - -func getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder { - encoder := _getTypeEncoderFromExtension(ctx, typ) - if encoder != nil { - for _, extension := range extensions { - encoder = extension.DecorateEncoder(typ, encoder) - } - encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder) - for _, extension := range ctx.extraExtensions { - encoder = extension.DecorateEncoder(typ, encoder) - } - } - return encoder -} - -func _getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder { - for _, extension := range extensions { - encoder := extension.CreateEncoder(typ) - if encoder != nil { - return encoder - } - } - encoder := ctx.encoderExtension.CreateEncoder(typ) - if encoder != nil { - return encoder - } - for _, extension := range ctx.extraExtensions { - encoder := extension.CreateEncoder(typ) - if encoder != nil { - return encoder - } - } - typeName := typ.String() - encoder = typeEncoders[typeName] - if encoder != nil { - return encoder - } - if typ.Kind() == reflect.Ptr { - typePtr := typ.(*reflect2.UnsafePtrType) - encoder := typeEncoders[typePtr.Elem().String()] - if encoder != nil { - return &OptionalEncoder{encoder} - } - } - return nil -} - -func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor { - structType := typ.(*reflect2.UnsafeStructType) - embeddedBindings := []*Binding{} - bindings := []*Binding{} - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - tag, hastag := field.Tag().Lookup(ctx.getTagKey()) - if ctx.onlyTaggedField && !hastag && !field.Anonymous() { - continue - } - if tag == "-" || field.Name() == "_" { - continue - } - tagParts := strings.Split(tag, ",") - if field.Anonymous() && (tag == "" || tagParts[0] == "") { - if field.Type().Kind() == reflect.Struct { - structDescriptor := describeStruct(ctx, field.Type()) - for _, binding := range structDescriptor.Fields { - binding.levels = append([]int{i}, binding.levels...) - omitempty := binding.Encoder.(*structFieldEncoder).omitempty - binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty} - binding.Decoder = &structFieldDecoder{field, binding.Decoder} - embeddedBindings = append(embeddedBindings, binding) - } - continue - } else if field.Type().Kind() == reflect.Ptr { - ptrType := field.Type().(*reflect2.UnsafePtrType) - if ptrType.Elem().Kind() == reflect.Struct { - structDescriptor := describeStruct(ctx, ptrType.Elem()) - for _, binding := range structDescriptor.Fields { - binding.levels = append([]int{i}, binding.levels...) - omitempty := binding.Encoder.(*structFieldEncoder).omitempty - binding.Encoder = &dereferenceEncoder{binding.Encoder} - binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty} - binding.Decoder = &dereferenceDecoder{ptrType.Elem(), binding.Decoder} - binding.Decoder = &structFieldDecoder{field, binding.Decoder} - embeddedBindings = append(embeddedBindings, binding) - } - continue - } - } - } - fieldNames := calcFieldNames(field.Name(), tagParts[0], tag) - fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name()) - decoder := fieldDecoders[fieldCacheKey] - if decoder == nil { - decoder = decoderOfType(ctx.append(field.Name()), field.Type()) - } - encoder := fieldEncoders[fieldCacheKey] - if encoder == nil { - encoder = encoderOfType(ctx.append(field.Name()), field.Type()) - } - binding := &Binding{ - Field: field, - FromNames: fieldNames, - ToNames: fieldNames, - Decoder: decoder, - Encoder: encoder, - } - binding.levels = []int{i} - bindings = append(bindings, binding) - } - return createStructDescriptor(ctx, typ, bindings, embeddedBindings) -} -func createStructDescriptor(ctx *ctx, typ reflect2.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor { - structDescriptor := &StructDescriptor{ - Type: typ, - Fields: bindings, - } - for _, extension := range extensions { - extension.UpdateStructDescriptor(structDescriptor) - } - ctx.encoderExtension.UpdateStructDescriptor(structDescriptor) - ctx.decoderExtension.UpdateStructDescriptor(structDescriptor) - for _, extension := range ctx.extraExtensions { - extension.UpdateStructDescriptor(structDescriptor) - } - processTags(structDescriptor, ctx.frozenConfig) - // merge normal & embedded bindings & sort with original order - allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...)) - sort.Sort(allBindings) - structDescriptor.Fields = allBindings - return structDescriptor -} - -type sortableBindings []*Binding - -func (bindings sortableBindings) Len() int { - return len(bindings) -} - -func (bindings sortableBindings) Less(i, j int) bool { - left := bindings[i].levels - right := bindings[j].levels - k := 0 - for { - if left[k] < right[k] { - return true - } else if left[k] > right[k] { - return false - } - k++ - } -} - -func (bindings sortableBindings) Swap(i, j int) { - bindings[i], bindings[j] = bindings[j], bindings[i] -} - -func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) { - for _, binding := range structDescriptor.Fields { - shouldOmitEmpty := false - tagParts := strings.Split(binding.Field.Tag().Get(cfg.getTagKey()), ",") - for _, tagPart := range tagParts[1:] { - if tagPart == "omitempty" { - shouldOmitEmpty = true - } else if tagPart == "string" { - if binding.Field.Type().Kind() == reflect.String { - binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg} - binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg} - } else { - binding.Decoder = &stringModeNumberDecoder{binding.Decoder} - binding.Encoder = &stringModeNumberEncoder{binding.Encoder} - } - } - } - binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder} - binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty} - } -} - -func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string { - // ignore? - if wholeTag == "-" { - return []string{} - } - // rename? - var fieldNames []string - if tagProvidedFieldName == "" { - fieldNames = []string{originalFieldName} - } else { - fieldNames = []string{tagProvidedFieldName} - } - // private? - isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_' - if isNotExported { - fieldNames = []string{} - } - return fieldNames -} diff --git a/vendor/github.com/json-iterator/go/reflect_json_number.go b/vendor/github.com/json-iterator/go/reflect_json_number.go deleted file mode 100644 index 98d45c1e..00000000 --- a/vendor/github.com/json-iterator/go/reflect_json_number.go +++ /dev/null @@ -1,112 +0,0 @@ -package jsoniter - -import ( - "encoding/json" - "github.com/modern-go/reflect2" - "strconv" - "unsafe" -) - -type Number string - -// String returns the literal text of the number. -func (n Number) String() string { return string(n) } - -// Float64 returns the number as a float64. -func (n Number) Float64() (float64, error) { - return strconv.ParseFloat(string(n), 64) -} - -// Int64 returns the number as an int64. -func (n Number) Int64() (int64, error) { - return strconv.ParseInt(string(n), 10, 64) -} - -func CastJsonNumber(val interface{}) (string, bool) { - switch typedVal := val.(type) { - case json.Number: - return string(typedVal), true - case Number: - return string(typedVal), true - } - return "", false -} - -var jsonNumberType = reflect2.TypeOfPtr((*json.Number)(nil)).Elem() -var jsoniterNumberType = reflect2.TypeOfPtr((*Number)(nil)).Elem() - -func createDecoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValDecoder { - if typ.AssignableTo(jsonNumberType) { - return &jsonNumberCodec{} - } - if typ.AssignableTo(jsoniterNumberType) { - return &jsoniterNumberCodec{} - } - return nil -} - -func createEncoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValEncoder { - if typ.AssignableTo(jsonNumberType) { - return &jsonNumberCodec{} - } - if typ.AssignableTo(jsoniterNumberType) { - return &jsoniterNumberCodec{} - } - return nil -} - -type jsonNumberCodec struct { -} - -func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - switch iter.WhatIsNext() { - case StringValue: - *((*json.Number)(ptr)) = json.Number(iter.ReadString()) - case NilValue: - iter.skipFourBytes('n', 'u', 'l', 'l') - *((*json.Number)(ptr)) = "" - default: - *((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString())) - } -} - -func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - number := *((*json.Number)(ptr)) - if len(number) == 0 { - stream.writeByte('0') - } else { - stream.WriteRaw(string(number)) - } -} - -func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { - return len(*((*json.Number)(ptr))) == 0 -} - -type jsoniterNumberCodec struct { -} - -func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - switch iter.WhatIsNext() { - case StringValue: - *((*Number)(ptr)) = Number(iter.ReadString()) - case NilValue: - iter.skipFourBytes('n', 'u', 'l', 'l') - *((*Number)(ptr)) = "" - default: - *((*Number)(ptr)) = Number([]byte(iter.readNumberAsString())) - } -} - -func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - number := *((*Number)(ptr)) - if len(number) == 0 { - stream.writeByte('0') - } else { - stream.WriteRaw(string(number)) - } -} - -func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { - return len(*((*Number)(ptr))) == 0 -} diff --git a/vendor/github.com/json-iterator/go/reflect_json_raw_message.go b/vendor/github.com/json-iterator/go/reflect_json_raw_message.go deleted file mode 100644 index eba434f2..00000000 --- a/vendor/github.com/json-iterator/go/reflect_json_raw_message.go +++ /dev/null @@ -1,76 +0,0 @@ -package jsoniter - -import ( - "encoding/json" - "github.com/modern-go/reflect2" - "unsafe" -) - -var jsonRawMessageType = reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem() -var jsoniterRawMessageType = reflect2.TypeOfPtr((*RawMessage)(nil)).Elem() - -func createEncoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValEncoder { - if typ == jsonRawMessageType { - return &jsonRawMessageCodec{} - } - if typ == jsoniterRawMessageType { - return &jsoniterRawMessageCodec{} - } - return nil -} - -func createDecoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValDecoder { - if typ == jsonRawMessageType { - return &jsonRawMessageCodec{} - } - if typ == jsoniterRawMessageType { - return &jsoniterRawMessageCodec{} - } - return nil -} - -type jsonRawMessageCodec struct { -} - -func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.ReadNil() { - *((*json.RawMessage)(ptr)) = nil - } else { - *((*json.RawMessage)(ptr)) = iter.SkipAndReturnBytes() - } -} - -func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - if *((*json.RawMessage)(ptr)) == nil { - stream.WriteNil() - } else { - stream.WriteRaw(string(*((*json.RawMessage)(ptr)))) - } -} - -func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { - return len(*((*json.RawMessage)(ptr))) == 0 -} - -type jsoniterRawMessageCodec struct { -} - -func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.ReadNil() { - *((*RawMessage)(ptr)) = nil - } else { - *((*RawMessage)(ptr)) = iter.SkipAndReturnBytes() - } -} - -func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - if *((*RawMessage)(ptr)) == nil { - stream.WriteNil() - } else { - stream.WriteRaw(string(*((*RawMessage)(ptr)))) - } -} - -func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { - return len(*((*RawMessage)(ptr))) == 0 -} diff --git a/vendor/github.com/json-iterator/go/reflect_map.go b/vendor/github.com/json-iterator/go/reflect_map.go deleted file mode 100644 index 58296713..00000000 --- a/vendor/github.com/json-iterator/go/reflect_map.go +++ /dev/null @@ -1,346 +0,0 @@ -package jsoniter - -import ( - "fmt" - "github.com/modern-go/reflect2" - "io" - "reflect" - "sort" - "unsafe" -) - -func decoderOfMap(ctx *ctx, typ reflect2.Type) ValDecoder { - mapType := typ.(*reflect2.UnsafeMapType) - keyDecoder := decoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()) - elemDecoder := decoderOfType(ctx.append("[mapElem]"), mapType.Elem()) - return &mapDecoder{ - mapType: mapType, - keyType: mapType.Key(), - elemType: mapType.Elem(), - keyDecoder: keyDecoder, - elemDecoder: elemDecoder, - } -} - -func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder { - mapType := typ.(*reflect2.UnsafeMapType) - if ctx.sortMapKeys { - return &sortKeysMapEncoder{ - mapType: mapType, - keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), - elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), - } - } - return &mapEncoder{ - mapType: mapType, - keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), - elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), - } -} - -func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { - decoder := ctx.decoderExtension.CreateMapKeyDecoder(typ) - if decoder != nil { - return decoder - } - for _, extension := range ctx.extraExtensions { - decoder := extension.CreateMapKeyDecoder(typ) - if decoder != nil { - return decoder - } - } - - ptrType := reflect2.PtrTo(typ) - if ptrType.Implements(unmarshalerType) { - return &referenceDecoder{ - &unmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(unmarshalerType) { - return &unmarshalerDecoder{ - valType: typ, - } - } - if ptrType.Implements(textUnmarshalerType) { - return &referenceDecoder{ - &textUnmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(textUnmarshalerType) { - return &textUnmarshalerDecoder{ - valType: typ, - } - } - - switch typ.Kind() { - case reflect.String: - return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) - case reflect.Bool, - reflect.Uint8, reflect.Int8, - reflect.Uint16, reflect.Int16, - reflect.Uint32, reflect.Int32, - reflect.Uint64, reflect.Int64, - reflect.Uint, reflect.Int, - reflect.Float32, reflect.Float64, - reflect.Uintptr: - typ = reflect2.DefaultTypeOfKind(typ.Kind()) - return &numericMapKeyDecoder{decoderOfType(ctx, typ)} - default: - return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} - } -} - -func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { - encoder := ctx.encoderExtension.CreateMapKeyEncoder(typ) - if encoder != nil { - return encoder - } - for _, extension := range ctx.extraExtensions { - encoder := extension.CreateMapKeyEncoder(typ) - if encoder != nil { - return encoder - } - } - - if typ == textMarshalerType { - return &directTextMarshalerEncoder{ - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } - if typ.Implements(textMarshalerType) { - return &textMarshalerEncoder{ - valType: typ, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } - - switch typ.Kind() { - case reflect.String: - return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) - case reflect.Bool, - reflect.Uint8, reflect.Int8, - reflect.Uint16, reflect.Int16, - reflect.Uint32, reflect.Int32, - reflect.Uint64, reflect.Int64, - reflect.Uint, reflect.Int, - reflect.Float32, reflect.Float64, - reflect.Uintptr: - typ = reflect2.DefaultTypeOfKind(typ.Kind()) - return &numericMapKeyEncoder{encoderOfType(ctx, typ)} - default: - if typ.Kind() == reflect.Interface { - return &dynamicMapKeyEncoder{ctx, typ} - } - return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)} - } -} - -type mapDecoder struct { - mapType *reflect2.UnsafeMapType - keyType reflect2.Type - elemType reflect2.Type - keyDecoder ValDecoder - elemDecoder ValDecoder -} - -func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - mapType := decoder.mapType - c := iter.nextToken() - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - *(*unsafe.Pointer)(ptr) = nil - mapType.UnsafeSet(ptr, mapType.UnsafeNew()) - return - } - if mapType.UnsafeIsNil(ptr) { - mapType.UnsafeSet(ptr, mapType.UnsafeMakeMap(0)) - } - if c != '{' { - iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c})) - return - } - c = iter.nextToken() - if c == '}' { - return - } - iter.unreadByte() - key := decoder.keyType.UnsafeNew() - decoder.keyDecoder.Decode(key, iter) - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) - return - } - elem := decoder.elemType.UnsafeNew() - decoder.elemDecoder.Decode(elem, iter) - decoder.mapType.UnsafeSetIndex(ptr, key, elem) - for c = iter.nextToken(); c == ','; c = iter.nextToken() { - key := decoder.keyType.UnsafeNew() - decoder.keyDecoder.Decode(key, iter) - c = iter.nextToken() - if c != ':' { - iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) - return - } - elem := decoder.elemType.UnsafeNew() - decoder.elemDecoder.Decode(elem, iter) - decoder.mapType.UnsafeSetIndex(ptr, key, elem) - } - if c != '}' { - iter.ReportError("ReadMapCB", `expect }, but found `+string([]byte{c})) - } -} - -type numericMapKeyDecoder struct { - decoder ValDecoder -} - -func (decoder *numericMapKeyDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - c := iter.nextToken() - if c != '"' { - iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c})) - return - } - decoder.decoder.Decode(ptr, iter) - c = iter.nextToken() - if c != '"' { - iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c})) - return - } -} - -type numericMapKeyEncoder struct { - encoder ValEncoder -} - -func (encoder *numericMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.writeByte('"') - encoder.encoder.Encode(ptr, stream) - stream.writeByte('"') -} - -func (encoder *numericMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return false -} - -type dynamicMapKeyEncoder struct { - ctx *ctx - valType reflect2.Type -} - -func (encoder *dynamicMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := encoder.valType.UnsafeIndirect(ptr) - encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).Encode(reflect2.PtrOf(obj), stream) -} - -func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool { - obj := encoder.valType.UnsafeIndirect(ptr) - return encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).IsEmpty(reflect2.PtrOf(obj)) -} - -type mapEncoder struct { - mapType *reflect2.UnsafeMapType - keyEncoder ValEncoder - elemEncoder ValEncoder -} - -func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if *(*unsafe.Pointer)(ptr) == nil { - stream.WriteNil() - return - } - stream.WriteObjectStart() - iter := encoder.mapType.UnsafeIterate(ptr) - for i := 0; iter.HasNext(); i++ { - if i != 0 { - stream.WriteMore() - } - key, elem := iter.UnsafeNext() - encoder.keyEncoder.Encode(key, stream) - if stream.indention > 0 { - stream.writeTwoBytes(byte(':'), byte(' ')) - } else { - stream.writeByte(':') - } - encoder.elemEncoder.Encode(elem, stream) - } - stream.WriteObjectEnd() -} - -func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool { - iter := encoder.mapType.UnsafeIterate(ptr) - return !iter.HasNext() -} - -type sortKeysMapEncoder struct { - mapType *reflect2.UnsafeMapType - keyEncoder ValEncoder - elemEncoder ValEncoder -} - -func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if *(*unsafe.Pointer)(ptr) == nil { - stream.WriteNil() - return - } - stream.WriteObjectStart() - mapIter := encoder.mapType.UnsafeIterate(ptr) - subStream := stream.cfg.BorrowStream(nil) - subStream.Attachment = stream.Attachment - subIter := stream.cfg.BorrowIterator(nil) - keyValues := encodedKeyValues{} - for mapIter.HasNext() { - key, elem := mapIter.UnsafeNext() - subStreamIndex := subStream.Buffered() - encoder.keyEncoder.Encode(key, subStream) - if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil { - stream.Error = subStream.Error - } - encodedKey := subStream.Buffer()[subStreamIndex:] - subIter.ResetBytes(encodedKey) - decodedKey := subIter.ReadString() - if stream.indention > 0 { - subStream.writeTwoBytes(byte(':'), byte(' ')) - } else { - subStream.writeByte(':') - } - encoder.elemEncoder.Encode(elem, subStream) - keyValues = append(keyValues, encodedKV{ - key: decodedKey, - keyValue: subStream.Buffer()[subStreamIndex:], - }) - } - sort.Sort(keyValues) - for i, keyValue := range keyValues { - if i != 0 { - stream.WriteMore() - } - stream.Write(keyValue.keyValue) - } - if subStream.Error != nil && stream.Error == nil { - stream.Error = subStream.Error - } - stream.WriteObjectEnd() - stream.cfg.ReturnStream(subStream) - stream.cfg.ReturnIterator(subIter) -} - -func (encoder *sortKeysMapEncoder) IsEmpty(ptr unsafe.Pointer) bool { - iter := encoder.mapType.UnsafeIterate(ptr) - return !iter.HasNext() -} - -type encodedKeyValues []encodedKV - -type encodedKV struct { - key string - keyValue []byte -} - -func (sv encodedKeyValues) Len() int { return len(sv) } -func (sv encodedKeyValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } -func (sv encodedKeyValues) Less(i, j int) bool { return sv[i].key < sv[j].key } diff --git a/vendor/github.com/json-iterator/go/reflect_marshaler.go b/vendor/github.com/json-iterator/go/reflect_marshaler.go deleted file mode 100644 index 3e21f375..00000000 --- a/vendor/github.com/json-iterator/go/reflect_marshaler.go +++ /dev/null @@ -1,225 +0,0 @@ -package jsoniter - -import ( - "encoding" - "encoding/json" - "unsafe" - - "github.com/modern-go/reflect2" -) - -var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem() -var unmarshalerType = reflect2.TypeOfPtr((*json.Unmarshaler)(nil)).Elem() -var textMarshalerType = reflect2.TypeOfPtr((*encoding.TextMarshaler)(nil)).Elem() -var textUnmarshalerType = reflect2.TypeOfPtr((*encoding.TextUnmarshaler)(nil)).Elem() - -func createDecoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValDecoder { - ptrType := reflect2.PtrTo(typ) - if ptrType.Implements(unmarshalerType) { - return &referenceDecoder{ - &unmarshalerDecoder{ptrType}, - } - } - if ptrType.Implements(textUnmarshalerType) { - return &referenceDecoder{ - &textUnmarshalerDecoder{ptrType}, - } - } - return nil -} - -func createEncoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValEncoder { - if typ == marshalerType { - checkIsEmpty := createCheckIsEmpty(ctx, typ) - var encoder ValEncoder = &directMarshalerEncoder{ - checkIsEmpty: checkIsEmpty, - } - return encoder - } - if typ.Implements(marshalerType) { - checkIsEmpty := createCheckIsEmpty(ctx, typ) - var encoder ValEncoder = &marshalerEncoder{ - valType: typ, - checkIsEmpty: checkIsEmpty, - } - return encoder - } - ptrType := reflect2.PtrTo(typ) - if ctx.prefix != "" && ptrType.Implements(marshalerType) { - checkIsEmpty := createCheckIsEmpty(ctx, ptrType) - var encoder ValEncoder = &marshalerEncoder{ - valType: ptrType, - checkIsEmpty: checkIsEmpty, - } - return &referenceEncoder{encoder} - } - if typ == textMarshalerType { - checkIsEmpty := createCheckIsEmpty(ctx, typ) - var encoder ValEncoder = &directTextMarshalerEncoder{ - checkIsEmpty: checkIsEmpty, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - return encoder - } - if typ.Implements(textMarshalerType) { - checkIsEmpty := createCheckIsEmpty(ctx, typ) - var encoder ValEncoder = &textMarshalerEncoder{ - valType: typ, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - checkIsEmpty: checkIsEmpty, - } - return encoder - } - // if prefix is empty, the type is the root type - if ctx.prefix != "" && ptrType.Implements(textMarshalerType) { - checkIsEmpty := createCheckIsEmpty(ctx, ptrType) - var encoder ValEncoder = &textMarshalerEncoder{ - valType: ptrType, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - checkIsEmpty: checkIsEmpty, - } - return &referenceEncoder{encoder} - } - return nil -} - -type marshalerEncoder struct { - checkIsEmpty checkIsEmpty - valType reflect2.Type -} - -func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := encoder.valType.UnsafeIndirect(ptr) - if encoder.valType.IsNullable() && reflect2.IsNil(obj) { - stream.WriteNil() - return - } - marshaler := obj.(json.Marshaler) - bytes, err := marshaler.MarshalJSON() - if err != nil { - stream.Error = err - } else { - // html escape was already done by jsoniter - // but the extra '\n' should be trimed - l := len(bytes) - if l > 0 && bytes[l-1] == '\n' { - bytes = bytes[:l-1] - } - stream.Write(bytes) - } -} - -func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.checkIsEmpty.IsEmpty(ptr) -} - -type directMarshalerEncoder struct { - checkIsEmpty checkIsEmpty -} - -func (encoder *directMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - marshaler := *(*json.Marshaler)(ptr) - if marshaler == nil { - stream.WriteNil() - return - } - bytes, err := marshaler.MarshalJSON() - if err != nil { - stream.Error = err - } else { - stream.Write(bytes) - } -} - -func (encoder *directMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.checkIsEmpty.IsEmpty(ptr) -} - -type textMarshalerEncoder struct { - valType reflect2.Type - stringEncoder ValEncoder - checkIsEmpty checkIsEmpty -} - -func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - obj := encoder.valType.UnsafeIndirect(ptr) - if encoder.valType.IsNullable() && reflect2.IsNil(obj) { - stream.WriteNil() - return - } - marshaler := (obj).(encoding.TextMarshaler) - bytes, err := marshaler.MarshalText() - if err != nil { - stream.Error = err - } else { - str := string(bytes) - encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) - } -} - -func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.checkIsEmpty.IsEmpty(ptr) -} - -type directTextMarshalerEncoder struct { - stringEncoder ValEncoder - checkIsEmpty checkIsEmpty -} - -func (encoder *directTextMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - marshaler := *(*encoding.TextMarshaler)(ptr) - if marshaler == nil { - stream.WriteNil() - return - } - bytes, err := marshaler.MarshalText() - if err != nil { - stream.Error = err - } else { - str := string(bytes) - encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) - } -} - -func (encoder *directTextMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.checkIsEmpty.IsEmpty(ptr) -} - -type unmarshalerDecoder struct { - valType reflect2.Type -} - -func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - valType := decoder.valType - obj := valType.UnsafeIndirect(ptr) - unmarshaler := obj.(json.Unmarshaler) - iter.nextToken() - iter.unreadByte() // skip spaces - bytes := iter.SkipAndReturnBytes() - err := unmarshaler.UnmarshalJSON(bytes) - if err != nil { - iter.ReportError("unmarshalerDecoder", err.Error()) - } -} - -type textUnmarshalerDecoder struct { - valType reflect2.Type -} - -func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - valType := decoder.valType - obj := valType.UnsafeIndirect(ptr) - if reflect2.IsNil(obj) { - ptrType := valType.(*reflect2.UnsafePtrType) - elemType := ptrType.Elem() - elem := elemType.UnsafeNew() - ptrType.UnsafeSet(ptr, unsafe.Pointer(&elem)) - obj = valType.UnsafeIndirect(ptr) - } - unmarshaler := (obj).(encoding.TextUnmarshaler) - str := iter.ReadString() - err := unmarshaler.UnmarshalText([]byte(str)) - if err != nil { - iter.ReportError("textUnmarshalerDecoder", err.Error()) - } -} diff --git a/vendor/github.com/json-iterator/go/reflect_native.go b/vendor/github.com/json-iterator/go/reflect_native.go deleted file mode 100644 index f88722d1..00000000 --- a/vendor/github.com/json-iterator/go/reflect_native.go +++ /dev/null @@ -1,453 +0,0 @@ -package jsoniter - -import ( - "encoding/base64" - "reflect" - "strconv" - "unsafe" - - "github.com/modern-go/reflect2" -) - -const ptrSize = 32 << uintptr(^uintptr(0)>>63) - -func createEncoderOfNative(ctx *ctx, typ reflect2.Type) ValEncoder { - if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 { - sliceDecoder := decoderOfSlice(ctx, typ) - return &base64Codec{sliceDecoder: sliceDecoder} - } - typeName := typ.String() - kind := typ.Kind() - switch kind { - case reflect.String: - if typeName != "string" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem()) - } - return &stringCodec{} - case reflect.Int: - if typeName != "int" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem()) - } - if strconv.IntSize == 32 { - return &int32Codec{} - } - return &int64Codec{} - case reflect.Int8: - if typeName != "int8" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem()) - } - return &int8Codec{} - case reflect.Int16: - if typeName != "int16" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem()) - } - return &int16Codec{} - case reflect.Int32: - if typeName != "int32" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem()) - } - return &int32Codec{} - case reflect.Int64: - if typeName != "int64" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem()) - } - return &int64Codec{} - case reflect.Uint: - if typeName != "uint" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem()) - } - if strconv.IntSize == 32 { - return &uint32Codec{} - } - return &uint64Codec{} - case reflect.Uint8: - if typeName != "uint8" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem()) - } - return &uint8Codec{} - case reflect.Uint16: - if typeName != "uint16" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem()) - } - return &uint16Codec{} - case reflect.Uint32: - if typeName != "uint32" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem()) - } - return &uint32Codec{} - case reflect.Uintptr: - if typeName != "uintptr" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem()) - } - if ptrSize == 32 { - return &uint32Codec{} - } - return &uint64Codec{} - case reflect.Uint64: - if typeName != "uint64" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem()) - } - return &uint64Codec{} - case reflect.Float32: - if typeName != "float32" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem()) - } - return &float32Codec{} - case reflect.Float64: - if typeName != "float64" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem()) - } - return &float64Codec{} - case reflect.Bool: - if typeName != "bool" { - return encoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem()) - } - return &boolCodec{} - } - return nil -} - -func createDecoderOfNative(ctx *ctx, typ reflect2.Type) ValDecoder { - if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 { - sliceDecoder := decoderOfSlice(ctx, typ) - return &base64Codec{sliceDecoder: sliceDecoder} - } - typeName := typ.String() - switch typ.Kind() { - case reflect.String: - if typeName != "string" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem()) - } - return &stringCodec{} - case reflect.Int: - if typeName != "int" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem()) - } - if strconv.IntSize == 32 { - return &int32Codec{} - } - return &int64Codec{} - case reflect.Int8: - if typeName != "int8" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem()) - } - return &int8Codec{} - case reflect.Int16: - if typeName != "int16" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem()) - } - return &int16Codec{} - case reflect.Int32: - if typeName != "int32" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem()) - } - return &int32Codec{} - case reflect.Int64: - if typeName != "int64" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem()) - } - return &int64Codec{} - case reflect.Uint: - if typeName != "uint" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem()) - } - if strconv.IntSize == 32 { - return &uint32Codec{} - } - return &uint64Codec{} - case reflect.Uint8: - if typeName != "uint8" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem()) - } - return &uint8Codec{} - case reflect.Uint16: - if typeName != "uint16" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem()) - } - return &uint16Codec{} - case reflect.Uint32: - if typeName != "uint32" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem()) - } - return &uint32Codec{} - case reflect.Uintptr: - if typeName != "uintptr" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem()) - } - if ptrSize == 32 { - return &uint32Codec{} - } - return &uint64Codec{} - case reflect.Uint64: - if typeName != "uint64" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem()) - } - return &uint64Codec{} - case reflect.Float32: - if typeName != "float32" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem()) - } - return &float32Codec{} - case reflect.Float64: - if typeName != "float64" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem()) - } - return &float64Codec{} - case reflect.Bool: - if typeName != "bool" { - return decoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem()) - } - return &boolCodec{} - } - return nil -} - -type stringCodec struct { -} - -func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - *((*string)(ptr)) = iter.ReadString() -} - -func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - str := *((*string)(ptr)) - stream.WriteString(str) -} - -func (codec *stringCodec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*string)(ptr)) == "" -} - -type int8Codec struct { -} - -func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*int8)(ptr)) = iter.ReadInt8() - } -} - -func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteInt8(*((*int8)(ptr))) -} - -func (codec *int8Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*int8)(ptr)) == 0 -} - -type int16Codec struct { -} - -func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*int16)(ptr)) = iter.ReadInt16() - } -} - -func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteInt16(*((*int16)(ptr))) -} - -func (codec *int16Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*int16)(ptr)) == 0 -} - -type int32Codec struct { -} - -func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*int32)(ptr)) = iter.ReadInt32() - } -} - -func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteInt32(*((*int32)(ptr))) -} - -func (codec *int32Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*int32)(ptr)) == 0 -} - -type int64Codec struct { -} - -func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*int64)(ptr)) = iter.ReadInt64() - } -} - -func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteInt64(*((*int64)(ptr))) -} - -func (codec *int64Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*int64)(ptr)) == 0 -} - -type uint8Codec struct { -} - -func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*uint8)(ptr)) = iter.ReadUint8() - } -} - -func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteUint8(*((*uint8)(ptr))) -} - -func (codec *uint8Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*uint8)(ptr)) == 0 -} - -type uint16Codec struct { -} - -func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*uint16)(ptr)) = iter.ReadUint16() - } -} - -func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteUint16(*((*uint16)(ptr))) -} - -func (codec *uint16Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*uint16)(ptr)) == 0 -} - -type uint32Codec struct { -} - -func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*uint32)(ptr)) = iter.ReadUint32() - } -} - -func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteUint32(*((*uint32)(ptr))) -} - -func (codec *uint32Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*uint32)(ptr)) == 0 -} - -type uint64Codec struct { -} - -func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*uint64)(ptr)) = iter.ReadUint64() - } -} - -func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteUint64(*((*uint64)(ptr))) -} - -func (codec *uint64Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*uint64)(ptr)) == 0 -} - -type float32Codec struct { -} - -func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*float32)(ptr)) = iter.ReadFloat32() - } -} - -func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteFloat32(*((*float32)(ptr))) -} - -func (codec *float32Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*float32)(ptr)) == 0 -} - -type float64Codec struct { -} - -func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*float64)(ptr)) = iter.ReadFloat64() - } -} - -func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteFloat64(*((*float64)(ptr))) -} - -func (codec *float64Codec) IsEmpty(ptr unsafe.Pointer) bool { - return *((*float64)(ptr)) == 0 -} - -type boolCodec struct { -} - -func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.ReadNil() { - *((*bool)(ptr)) = iter.ReadBool() - } -} - -func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteBool(*((*bool)(ptr))) -} - -func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool { - return !(*((*bool)(ptr))) -} - -type base64Codec struct { - sliceType *reflect2.UnsafeSliceType - sliceDecoder ValDecoder -} - -func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.ReadNil() { - codec.sliceType.UnsafeSetNil(ptr) - return - } - switch iter.WhatIsNext() { - case StringValue: - src := iter.ReadString() - dst, err := base64.StdEncoding.DecodeString(src) - if err != nil { - iter.ReportError("decode base64", err.Error()) - } else { - codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst)) - } - case ArrayValue: - codec.sliceDecoder.Decode(ptr, iter) - default: - iter.ReportError("base64Codec", "invalid input") - } -} - -func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { - if codec.sliceType.UnsafeIsNil(ptr) { - stream.WriteNil() - return - } - src := *((*[]byte)(ptr)) - encoding := base64.StdEncoding - stream.writeByte('"') - if len(src) != 0 { - size := encoding.EncodedLen(len(src)) - buf := make([]byte, size) - encoding.Encode(buf, src) - stream.buf = append(stream.buf, buf...) - } - stream.writeByte('"') -} - -func (codec *base64Codec) IsEmpty(ptr unsafe.Pointer) bool { - return len(*((*[]byte)(ptr))) == 0 -} diff --git a/vendor/github.com/json-iterator/go/reflect_optional.go b/vendor/github.com/json-iterator/go/reflect_optional.go deleted file mode 100644 index fa71f474..00000000 --- a/vendor/github.com/json-iterator/go/reflect_optional.go +++ /dev/null @@ -1,129 +0,0 @@ -package jsoniter - -import ( - "github.com/modern-go/reflect2" - "unsafe" -) - -func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { - ptrType := typ.(*reflect2.UnsafePtrType) - elemType := ptrType.Elem() - decoder := decoderOfType(ctx, elemType) - return &OptionalDecoder{elemType, decoder} -} - -func encoderOfOptional(ctx *ctx, typ reflect2.Type) ValEncoder { - ptrType := typ.(*reflect2.UnsafePtrType) - elemType := ptrType.Elem() - elemEncoder := encoderOfType(ctx, elemType) - encoder := &OptionalEncoder{elemEncoder} - return encoder -} - -type OptionalDecoder struct { - ValueType reflect2.Type - ValueDecoder ValDecoder -} - -func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.ReadNil() { - *((*unsafe.Pointer)(ptr)) = nil - } else { - if *((*unsafe.Pointer)(ptr)) == nil { - //pointer to null, we have to allocate memory to hold the value - newPtr := decoder.ValueType.UnsafeNew() - decoder.ValueDecoder.Decode(newPtr, iter) - *((*unsafe.Pointer)(ptr)) = newPtr - } else { - //reuse existing instance - decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) - } - } -} - -type dereferenceDecoder struct { - // only to deference a pointer - valueType reflect2.Type - valueDecoder ValDecoder -} - -func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if *((*unsafe.Pointer)(ptr)) == nil { - //pointer to null, we have to allocate memory to hold the value - newPtr := decoder.valueType.UnsafeNew() - decoder.valueDecoder.Decode(newPtr, iter) - *((*unsafe.Pointer)(ptr)) = newPtr - } else { - //reuse existing instance - decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) - } -} - -type OptionalEncoder struct { - ValueEncoder ValEncoder -} - -func (encoder *OptionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if *((*unsafe.Pointer)(ptr)) == nil { - stream.WriteNil() - } else { - encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) - } -} - -func (encoder *OptionalEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return *((*unsafe.Pointer)(ptr)) == nil -} - -type dereferenceEncoder struct { - ValueEncoder ValEncoder -} - -func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if *((*unsafe.Pointer)(ptr)) == nil { - stream.WriteNil() - } else { - encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) - } -} - -func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { - dePtr := *((*unsafe.Pointer)(ptr)) - if dePtr == nil { - return true - } - return encoder.ValueEncoder.IsEmpty(dePtr) -} - -func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { - deReferenced := *((*unsafe.Pointer)(ptr)) - if deReferenced == nil { - return true - } - isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil) - if !converted { - return false - } - fieldPtr := unsafe.Pointer(deReferenced) - return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) -} - -type referenceEncoder struct { - encoder ValEncoder -} - -func (encoder *referenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - encoder.encoder.Encode(unsafe.Pointer(&ptr), stream) -} - -func (encoder *referenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr)) -} - -type referenceDecoder struct { - decoder ValDecoder -} - -func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.decoder.Decode(unsafe.Pointer(&ptr), iter) -} diff --git a/vendor/github.com/json-iterator/go/reflect_slice.go b/vendor/github.com/json-iterator/go/reflect_slice.go deleted file mode 100644 index 9441d79d..00000000 --- a/vendor/github.com/json-iterator/go/reflect_slice.go +++ /dev/null @@ -1,99 +0,0 @@ -package jsoniter - -import ( - "fmt" - "github.com/modern-go/reflect2" - "io" - "unsafe" -) - -func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder { - sliceType := typ.(*reflect2.UnsafeSliceType) - decoder := decoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) - return &sliceDecoder{sliceType, decoder} -} - -func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder { - sliceType := typ.(*reflect2.UnsafeSliceType) - encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) - return &sliceEncoder{sliceType, encoder} -} - -type sliceEncoder struct { - sliceType *reflect2.UnsafeSliceType - elemEncoder ValEncoder -} - -func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - if encoder.sliceType.UnsafeIsNil(ptr) { - stream.WriteNil() - return - } - length := encoder.sliceType.UnsafeLengthOf(ptr) - if length == 0 { - stream.WriteEmptyArray() - return - } - stream.WriteArrayStart() - encoder.elemEncoder.Encode(encoder.sliceType.UnsafeGetIndex(ptr, 0), stream) - for i := 1; i < length; i++ { - stream.WriteMore() - elemPtr := encoder.sliceType.UnsafeGetIndex(ptr, i) - encoder.elemEncoder.Encode(elemPtr, stream) - } - stream.WriteArrayEnd() - if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) - } -} - -func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.sliceType.UnsafeLengthOf(ptr) == 0 -} - -type sliceDecoder struct { - sliceType *reflect2.UnsafeSliceType - elemDecoder ValDecoder -} - -func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.doDecode(ptr, iter) - if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) - } -} - -func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { - c := iter.nextToken() - sliceType := decoder.sliceType - if c == 'n' { - iter.skipThreeBytes('u', 'l', 'l') - sliceType.UnsafeSetNil(ptr) - return - } - if c != '[' { - iter.ReportError("decode slice", "expect [ or n, but found "+string([]byte{c})) - return - } - c = iter.nextToken() - if c == ']' { - sliceType.UnsafeSet(ptr, sliceType.UnsafeMakeSlice(0, 0)) - return - } - iter.unreadByte() - sliceType.UnsafeGrow(ptr, 1) - elemPtr := sliceType.UnsafeGetIndex(ptr, 0) - decoder.elemDecoder.Decode(elemPtr, iter) - length := 1 - for c = iter.nextToken(); c == ','; c = iter.nextToken() { - idx := length - length += 1 - sliceType.UnsafeGrow(ptr, length) - elemPtr = sliceType.UnsafeGetIndex(ptr, idx) - decoder.elemDecoder.Decode(elemPtr, iter) - } - if c != ']' { - iter.ReportError("decode slice", "expect ], but found "+string([]byte{c})) - return - } -} diff --git a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go deleted file mode 100644 index 92ae912d..00000000 --- a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go +++ /dev/null @@ -1,1097 +0,0 @@ -package jsoniter - -import ( - "fmt" - "io" - "strings" - "unsafe" - - "github.com/modern-go/reflect2" -) - -func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { - bindings := map[string]*Binding{} - structDescriptor := describeStruct(ctx, typ) - for _, binding := range structDescriptor.Fields { - for _, fromName := range binding.FromNames { - old := bindings[fromName] - if old == nil { - bindings[fromName] = binding - continue - } - ignoreOld, ignoreNew := resolveConflictBinding(ctx.frozenConfig, old, binding) - if ignoreOld { - delete(bindings, fromName) - } - if !ignoreNew { - bindings[fromName] = binding - } - } - } - fields := map[string]*structFieldDecoder{} - for k, binding := range bindings { - fields[k] = binding.Decoder.(*structFieldDecoder) - } - - if !ctx.caseSensitive() { - for k, binding := range bindings { - if _, found := fields[strings.ToLower(k)]; !found { - fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder) - } - } - } - - return createStructDecoder(ctx, typ, fields) -} - -func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structFieldDecoder) ValDecoder { - if ctx.disallowUnknownFields { - return &generalStructDecoder{typ: typ, fields: fields, disallowUnknownFields: true} - } - knownHash := map[int64]struct{}{ - 0: {}, - } - - switch len(fields) { - case 0: - return &skipObjectDecoder{typ} - case 1: - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder} - } - case 2: - var fieldHash1 int64 - var fieldHash2 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldHash1 == 0 { - fieldHash1 = fieldHash - fieldDecoder1 = fieldDecoder - } else { - fieldHash2 = fieldHash - fieldDecoder2 = fieldDecoder - } - } - return &twoFieldsStructDecoder{typ, fieldHash1, fieldDecoder1, fieldHash2, fieldDecoder2} - case 3: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } - } - return &threeFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3} - case 4: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } - } - return &fourFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4} - case 5: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } - } - return &fiveFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5} - case 6: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldName6 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - var fieldDecoder6 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else if fieldName5 == 0 { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } else { - fieldName6 = fieldHash - fieldDecoder6 = fieldDecoder - } - } - return &sixFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5, - fieldName6, fieldDecoder6} - case 7: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldName6 int64 - var fieldName7 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - var fieldDecoder6 *structFieldDecoder - var fieldDecoder7 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else if fieldName5 == 0 { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } else if fieldName6 == 0 { - fieldName6 = fieldHash - fieldDecoder6 = fieldDecoder - } else { - fieldName7 = fieldHash - fieldDecoder7 = fieldDecoder - } - } - return &sevenFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5, - fieldName6, fieldDecoder6, - fieldName7, fieldDecoder7} - case 8: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldName6 int64 - var fieldName7 int64 - var fieldName8 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - var fieldDecoder6 *structFieldDecoder - var fieldDecoder7 *structFieldDecoder - var fieldDecoder8 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else if fieldName5 == 0 { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } else if fieldName6 == 0 { - fieldName6 = fieldHash - fieldDecoder6 = fieldDecoder - } else if fieldName7 == 0 { - fieldName7 = fieldHash - fieldDecoder7 = fieldDecoder - } else { - fieldName8 = fieldHash - fieldDecoder8 = fieldDecoder - } - } - return &eightFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5, - fieldName6, fieldDecoder6, - fieldName7, fieldDecoder7, - fieldName8, fieldDecoder8} - case 9: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldName6 int64 - var fieldName7 int64 - var fieldName8 int64 - var fieldName9 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - var fieldDecoder6 *structFieldDecoder - var fieldDecoder7 *structFieldDecoder - var fieldDecoder8 *structFieldDecoder - var fieldDecoder9 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else if fieldName5 == 0 { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } else if fieldName6 == 0 { - fieldName6 = fieldHash - fieldDecoder6 = fieldDecoder - } else if fieldName7 == 0 { - fieldName7 = fieldHash - fieldDecoder7 = fieldDecoder - } else if fieldName8 == 0 { - fieldName8 = fieldHash - fieldDecoder8 = fieldDecoder - } else { - fieldName9 = fieldHash - fieldDecoder9 = fieldDecoder - } - } - return &nineFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5, - fieldName6, fieldDecoder6, - fieldName7, fieldDecoder7, - fieldName8, fieldDecoder8, - fieldName9, fieldDecoder9} - case 10: - var fieldName1 int64 - var fieldName2 int64 - var fieldName3 int64 - var fieldName4 int64 - var fieldName5 int64 - var fieldName6 int64 - var fieldName7 int64 - var fieldName8 int64 - var fieldName9 int64 - var fieldName10 int64 - var fieldDecoder1 *structFieldDecoder - var fieldDecoder2 *structFieldDecoder - var fieldDecoder3 *structFieldDecoder - var fieldDecoder4 *structFieldDecoder - var fieldDecoder5 *structFieldDecoder - var fieldDecoder6 *structFieldDecoder - var fieldDecoder7 *structFieldDecoder - var fieldDecoder8 *structFieldDecoder - var fieldDecoder9 *structFieldDecoder - var fieldDecoder10 *structFieldDecoder - for fieldName, fieldDecoder := range fields { - fieldHash := calcHash(fieldName, ctx.caseSensitive()) - _, known := knownHash[fieldHash] - if known { - return &generalStructDecoder{typ, fields, false} - } - knownHash[fieldHash] = struct{}{} - if fieldName1 == 0 { - fieldName1 = fieldHash - fieldDecoder1 = fieldDecoder - } else if fieldName2 == 0 { - fieldName2 = fieldHash - fieldDecoder2 = fieldDecoder - } else if fieldName3 == 0 { - fieldName3 = fieldHash - fieldDecoder3 = fieldDecoder - } else if fieldName4 == 0 { - fieldName4 = fieldHash - fieldDecoder4 = fieldDecoder - } else if fieldName5 == 0 { - fieldName5 = fieldHash - fieldDecoder5 = fieldDecoder - } else if fieldName6 == 0 { - fieldName6 = fieldHash - fieldDecoder6 = fieldDecoder - } else if fieldName7 == 0 { - fieldName7 = fieldHash - fieldDecoder7 = fieldDecoder - } else if fieldName8 == 0 { - fieldName8 = fieldHash - fieldDecoder8 = fieldDecoder - } else if fieldName9 == 0 { - fieldName9 = fieldHash - fieldDecoder9 = fieldDecoder - } else { - fieldName10 = fieldHash - fieldDecoder10 = fieldDecoder - } - } - return &tenFieldsStructDecoder{typ, - fieldName1, fieldDecoder1, - fieldName2, fieldDecoder2, - fieldName3, fieldDecoder3, - fieldName4, fieldDecoder4, - fieldName5, fieldDecoder5, - fieldName6, fieldDecoder6, - fieldName7, fieldDecoder7, - fieldName8, fieldDecoder8, - fieldName9, fieldDecoder9, - fieldName10, fieldDecoder10} - } - return &generalStructDecoder{typ, fields, false} -} - -type generalStructDecoder struct { - typ reflect2.Type - fields map[string]*structFieldDecoder - disallowUnknownFields bool -} - -func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - var c byte - for c = ','; c == ','; c = iter.nextToken() { - decoder.decodeOneField(ptr, iter) - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - if c != '}' { - iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c})) - } - iter.decrementDepth() -} - -func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) { - var field string - var fieldDecoder *structFieldDecoder - if iter.cfg.objectFieldMustBeSimpleString { - fieldBytes := iter.ReadStringAsSlice() - field = *(*string)(unsafe.Pointer(&fieldBytes)) - fieldDecoder = decoder.fields[field] - if fieldDecoder == nil && !iter.cfg.caseSensitive { - fieldDecoder = decoder.fields[strings.ToLower(field)] - } - } else { - field = iter.ReadString() - fieldDecoder = decoder.fields[field] - if fieldDecoder == nil && !iter.cfg.caseSensitive { - fieldDecoder = decoder.fields[strings.ToLower(field)] - } - } - if fieldDecoder == nil { - if decoder.disallowUnknownFields { - msg := "found unknown field: " + field - iter.ReportError("ReadObject", msg) - } - c := iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - iter.Skip() - return - } - c := iter.nextToken() - if c != ':' { - iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) - } - fieldDecoder.Decode(ptr, iter) -} - -type skipObjectDecoder struct { - typ reflect2.Type -} - -func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - valueType := iter.WhatIsNext() - if valueType != ObjectValue && valueType != NilValue { - iter.ReportError("skipObjectDecoder", "expect object or null") - return - } - iter.Skip() -} - -type oneFieldStructDecoder struct { - typ reflect2.Type - fieldHash int64 - fieldDecoder *structFieldDecoder -} - -func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - if iter.readFieldHash() == decoder.fieldHash { - decoder.fieldDecoder.Decode(ptr, iter) - } else { - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type twoFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder -} - -func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type threeFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder -} - -func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type fourFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder -} - -func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type fiveFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder -} - -func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type sixFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder - fieldHash6 int64 - fieldDecoder6 *structFieldDecoder -} - -func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - case decoder.fieldHash6: - decoder.fieldDecoder6.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type sevenFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder - fieldHash6 int64 - fieldDecoder6 *structFieldDecoder - fieldHash7 int64 - fieldDecoder7 *structFieldDecoder -} - -func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - case decoder.fieldHash6: - decoder.fieldDecoder6.Decode(ptr, iter) - case decoder.fieldHash7: - decoder.fieldDecoder7.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type eightFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder - fieldHash6 int64 - fieldDecoder6 *structFieldDecoder - fieldHash7 int64 - fieldDecoder7 *structFieldDecoder - fieldHash8 int64 - fieldDecoder8 *structFieldDecoder -} - -func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - case decoder.fieldHash6: - decoder.fieldDecoder6.Decode(ptr, iter) - case decoder.fieldHash7: - decoder.fieldDecoder7.Decode(ptr, iter) - case decoder.fieldHash8: - decoder.fieldDecoder8.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type nineFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder - fieldHash6 int64 - fieldDecoder6 *structFieldDecoder - fieldHash7 int64 - fieldDecoder7 *structFieldDecoder - fieldHash8 int64 - fieldDecoder8 *structFieldDecoder - fieldHash9 int64 - fieldDecoder9 *structFieldDecoder -} - -func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - case decoder.fieldHash6: - decoder.fieldDecoder6.Decode(ptr, iter) - case decoder.fieldHash7: - decoder.fieldDecoder7.Decode(ptr, iter) - case decoder.fieldHash8: - decoder.fieldDecoder8.Decode(ptr, iter) - case decoder.fieldHash9: - decoder.fieldDecoder9.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type tenFieldsStructDecoder struct { - typ reflect2.Type - fieldHash1 int64 - fieldDecoder1 *structFieldDecoder - fieldHash2 int64 - fieldDecoder2 *structFieldDecoder - fieldHash3 int64 - fieldDecoder3 *structFieldDecoder - fieldHash4 int64 - fieldDecoder4 *structFieldDecoder - fieldHash5 int64 - fieldDecoder5 *structFieldDecoder - fieldHash6 int64 - fieldDecoder6 *structFieldDecoder - fieldHash7 int64 - fieldDecoder7 *structFieldDecoder - fieldHash8 int64 - fieldDecoder8 *structFieldDecoder - fieldHash9 int64 - fieldDecoder9 *structFieldDecoder - fieldHash10 int64 - fieldDecoder10 *structFieldDecoder -} - -func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if !iter.readObjectStart() { - return - } - if !iter.incrementDepth() { - return - } - for { - switch iter.readFieldHash() { - case decoder.fieldHash1: - decoder.fieldDecoder1.Decode(ptr, iter) - case decoder.fieldHash2: - decoder.fieldDecoder2.Decode(ptr, iter) - case decoder.fieldHash3: - decoder.fieldDecoder3.Decode(ptr, iter) - case decoder.fieldHash4: - decoder.fieldDecoder4.Decode(ptr, iter) - case decoder.fieldHash5: - decoder.fieldDecoder5.Decode(ptr, iter) - case decoder.fieldHash6: - decoder.fieldDecoder6.Decode(ptr, iter) - case decoder.fieldHash7: - decoder.fieldDecoder7.Decode(ptr, iter) - case decoder.fieldHash8: - decoder.fieldDecoder8.Decode(ptr, iter) - case decoder.fieldHash9: - decoder.fieldDecoder9.Decode(ptr, iter) - case decoder.fieldHash10: - decoder.fieldDecoder10.Decode(ptr, iter) - default: - iter.Skip() - } - if iter.isObjectEnd() { - break - } - } - if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) - } - iter.decrementDepth() -} - -type structFieldDecoder struct { - field reflect2.StructField - fieldDecoder ValDecoder -} - -func (decoder *structFieldDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - fieldPtr := decoder.field.UnsafeGet(ptr) - decoder.fieldDecoder.Decode(fieldPtr, iter) - if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%s: %s", decoder.field.Name(), iter.Error.Error()) - } -} - -type stringModeStringDecoder struct { - elemDecoder ValDecoder - cfg *frozenConfig -} - -func (decoder *stringModeStringDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - decoder.elemDecoder.Decode(ptr, iter) - str := *((*string)(ptr)) - tempIter := decoder.cfg.BorrowIterator([]byte(str)) - defer decoder.cfg.ReturnIterator(tempIter) - *((*string)(ptr)) = tempIter.ReadString() -} - -type stringModeNumberDecoder struct { - elemDecoder ValDecoder -} - -func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { - if iter.WhatIsNext() == NilValue { - decoder.elemDecoder.Decode(ptr, iter) - return - } - - c := iter.nextToken() - if c != '"' { - iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c})) - return - } - decoder.elemDecoder.Decode(ptr, iter) - if iter.Error != nil { - return - } - c = iter.readByte() - if c != '"' { - iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c})) - return - } -} diff --git a/vendor/github.com/json-iterator/go/reflect_struct_encoder.go b/vendor/github.com/json-iterator/go/reflect_struct_encoder.go deleted file mode 100644 index 152e3ef5..00000000 --- a/vendor/github.com/json-iterator/go/reflect_struct_encoder.go +++ /dev/null @@ -1,211 +0,0 @@ -package jsoniter - -import ( - "fmt" - "github.com/modern-go/reflect2" - "io" - "reflect" - "unsafe" -) - -func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder { - type bindingTo struct { - binding *Binding - toName string - ignored bool - } - orderedBindings := []*bindingTo{} - structDescriptor := describeStruct(ctx, typ) - for _, binding := range structDescriptor.Fields { - for _, toName := range binding.ToNames { - new := &bindingTo{ - binding: binding, - toName: toName, - } - for _, old := range orderedBindings { - if old.toName != toName { - continue - } - old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding) - } - orderedBindings = append(orderedBindings, new) - } - } - if len(orderedBindings) == 0 { - return &emptyStructEncoder{} - } - finalOrderedFields := []structFieldTo{} - for _, bindingTo := range orderedBindings { - if !bindingTo.ignored { - finalOrderedFields = append(finalOrderedFields, structFieldTo{ - encoder: bindingTo.binding.Encoder.(*structFieldEncoder), - toName: bindingTo.toName, - }) - } - } - return &structEncoder{typ, finalOrderedFields} -} - -func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty { - encoder := createEncoderOfNative(ctx, typ) - if encoder != nil { - return encoder - } - kind := typ.Kind() - switch kind { - case reflect.Interface: - return &dynamicEncoder{typ} - case reflect.Struct: - return &structEncoder{typ: typ} - case reflect.Array: - return &arrayEncoder{} - case reflect.Slice: - return &sliceEncoder{} - case reflect.Map: - return encoderOfMap(ctx, typ) - case reflect.Ptr: - return &OptionalEncoder{} - default: - return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)} - } -} - -func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) { - newTagged := new.Field.Tag().Get(cfg.getTagKey()) != "" - oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != "" - if newTagged { - if oldTagged { - if len(old.levels) > len(new.levels) { - return true, false - } else if len(new.levels) > len(old.levels) { - return false, true - } else { - return true, true - } - } else { - return true, false - } - } else { - if oldTagged { - return true, false - } - if len(old.levels) > len(new.levels) { - return true, false - } else if len(new.levels) > len(old.levels) { - return false, true - } else { - return true, true - } - } -} - -type structFieldEncoder struct { - field reflect2.StructField - fieldEncoder ValEncoder - omitempty bool -} - -func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - fieldPtr := encoder.field.UnsafeGet(ptr) - encoder.fieldEncoder.Encode(fieldPtr, stream) - if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error()) - } -} - -func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool { - fieldPtr := encoder.field.UnsafeGet(ptr) - return encoder.fieldEncoder.IsEmpty(fieldPtr) -} - -func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { - isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil) - if !converted { - return false - } - fieldPtr := encoder.field.UnsafeGet(ptr) - return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) -} - -type IsEmbeddedPtrNil interface { - IsEmbeddedPtrNil(ptr unsafe.Pointer) bool -} - -type structEncoder struct { - typ reflect2.Type - fields []structFieldTo -} - -type structFieldTo struct { - encoder *structFieldEncoder - toName string -} - -func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteObjectStart() - isNotFirst := false - for _, field := range encoder.fields { - if field.encoder.omitempty && field.encoder.IsEmpty(ptr) { - continue - } - if field.encoder.IsEmbeddedPtrNil(ptr) { - continue - } - if isNotFirst { - stream.WriteMore() - } - stream.WriteObjectField(field.toName) - field.encoder.Encode(ptr, stream) - isNotFirst = true - } - stream.WriteObjectEnd() - if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error()) - } -} - -func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return false -} - -type emptyStructEncoder struct { -} - -func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.WriteEmptyObject() -} - -func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return false -} - -type stringModeNumberEncoder struct { - elemEncoder ValEncoder -} - -func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - stream.writeByte('"') - encoder.elemEncoder.Encode(ptr, stream) - stream.writeByte('"') -} - -func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.elemEncoder.IsEmpty(ptr) -} - -type stringModeStringEncoder struct { - elemEncoder ValEncoder - cfg *frozenConfig -} - -func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { - tempStream := encoder.cfg.BorrowStream(nil) - tempStream.Attachment = stream.Attachment - defer encoder.cfg.ReturnStream(tempStream) - encoder.elemEncoder.Encode(ptr, tempStream) - stream.WriteString(string(tempStream.Buffer())) -} - -func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool { - return encoder.elemEncoder.IsEmpty(ptr) -} diff --git a/vendor/github.com/json-iterator/go/stream.go b/vendor/github.com/json-iterator/go/stream.go deleted file mode 100644 index 23d8a3ad..00000000 --- a/vendor/github.com/json-iterator/go/stream.go +++ /dev/null @@ -1,210 +0,0 @@ -package jsoniter - -import ( - "io" -) - -// stream is a io.Writer like object, with JSON specific write functions. -// Error is not returned as return value, but stored as Error member on this stream instance. -type Stream struct { - cfg *frozenConfig - out io.Writer - buf []byte - Error error - indention int - Attachment interface{} // open for customized encoder -} - -// NewStream create new stream instance. -// cfg can be jsoniter.ConfigDefault. -// out can be nil if write to internal buffer. -// bufSize is the initial size for the internal buffer in bytes. -func NewStream(cfg API, out io.Writer, bufSize int) *Stream { - return &Stream{ - cfg: cfg.(*frozenConfig), - out: out, - buf: make([]byte, 0, bufSize), - Error: nil, - indention: 0, - } -} - -// Pool returns a pool can provide more stream with same configuration -func (stream *Stream) Pool() StreamPool { - return stream.cfg -} - -// Reset reuse this stream instance by assign a new writer -func (stream *Stream) Reset(out io.Writer) { - stream.out = out - stream.buf = stream.buf[:0] -} - -// Available returns how many bytes are unused in the buffer. -func (stream *Stream) Available() int { - return cap(stream.buf) - len(stream.buf) -} - -// Buffered returns the number of bytes that have been written into the current buffer. -func (stream *Stream) Buffered() int { - return len(stream.buf) -} - -// Buffer if writer is nil, use this method to take the result -func (stream *Stream) Buffer() []byte { - return stream.buf -} - -// SetBuffer allows to append to the internal buffer directly -func (stream *Stream) SetBuffer(buf []byte) { - stream.buf = buf -} - -// Write writes the contents of p into the buffer. -// It returns the number of bytes written. -// If nn < len(p), it also returns an error explaining -// why the write is short. -func (stream *Stream) Write(p []byte) (nn int, err error) { - stream.buf = append(stream.buf, p...) - if stream.out != nil { - nn, err = stream.out.Write(stream.buf) - stream.buf = stream.buf[nn:] - return - } - return len(p), nil -} - -// WriteByte writes a single byte. -func (stream *Stream) writeByte(c byte) { - stream.buf = append(stream.buf, c) -} - -func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) { - stream.buf = append(stream.buf, c1, c2) -} - -func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) { - stream.buf = append(stream.buf, c1, c2, c3) -} - -func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) { - stream.buf = append(stream.buf, c1, c2, c3, c4) -} - -func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) { - stream.buf = append(stream.buf, c1, c2, c3, c4, c5) -} - -// Flush writes any buffered data to the underlying io.Writer. -func (stream *Stream) Flush() error { - if stream.out == nil { - return nil - } - if stream.Error != nil { - return stream.Error - } - _, err := stream.out.Write(stream.buf) - if err != nil { - if stream.Error == nil { - stream.Error = err - } - return err - } - stream.buf = stream.buf[:0] - return nil -} - -// WriteRaw write string out without quotes, just like []byte -func (stream *Stream) WriteRaw(s string) { - stream.buf = append(stream.buf, s...) -} - -// WriteNil write null to stream -func (stream *Stream) WriteNil() { - stream.writeFourBytes('n', 'u', 'l', 'l') -} - -// WriteTrue write true to stream -func (stream *Stream) WriteTrue() { - stream.writeFourBytes('t', 'r', 'u', 'e') -} - -// WriteFalse write false to stream -func (stream *Stream) WriteFalse() { - stream.writeFiveBytes('f', 'a', 'l', 's', 'e') -} - -// WriteBool write true or false into stream -func (stream *Stream) WriteBool(val bool) { - if val { - stream.WriteTrue() - } else { - stream.WriteFalse() - } -} - -// WriteObjectStart write { with possible indention -func (stream *Stream) WriteObjectStart() { - stream.indention += stream.cfg.indentionStep - stream.writeByte('{') - stream.writeIndention(0) -} - -// WriteObjectField write "field": with possible indention -func (stream *Stream) WriteObjectField(field string) { - stream.WriteString(field) - if stream.indention > 0 { - stream.writeTwoBytes(':', ' ') - } else { - stream.writeByte(':') - } -} - -// WriteObjectEnd write } with possible indention -func (stream *Stream) WriteObjectEnd() { - stream.writeIndention(stream.cfg.indentionStep) - stream.indention -= stream.cfg.indentionStep - stream.writeByte('}') -} - -// WriteEmptyObject write {} -func (stream *Stream) WriteEmptyObject() { - stream.writeByte('{') - stream.writeByte('}') -} - -// WriteMore write , with possible indention -func (stream *Stream) WriteMore() { - stream.writeByte(',') - stream.writeIndention(0) -} - -// WriteArrayStart write [ with possible indention -func (stream *Stream) WriteArrayStart() { - stream.indention += stream.cfg.indentionStep - stream.writeByte('[') - stream.writeIndention(0) -} - -// WriteEmptyArray write [] -func (stream *Stream) WriteEmptyArray() { - stream.writeTwoBytes('[', ']') -} - -// WriteArrayEnd write ] with possible indention -func (stream *Stream) WriteArrayEnd() { - stream.writeIndention(stream.cfg.indentionStep) - stream.indention -= stream.cfg.indentionStep - stream.writeByte(']') -} - -func (stream *Stream) writeIndention(delta int) { - if stream.indention == 0 { - return - } - stream.writeByte('\n') - toWrite := stream.indention - delta - for i := 0; i < toWrite; i++ { - stream.buf = append(stream.buf, ' ') - } -} diff --git a/vendor/github.com/json-iterator/go/stream_float.go b/vendor/github.com/json-iterator/go/stream_float.go deleted file mode 100644 index 826aa594..00000000 --- a/vendor/github.com/json-iterator/go/stream_float.go +++ /dev/null @@ -1,111 +0,0 @@ -package jsoniter - -import ( - "fmt" - "math" - "strconv" -) - -var pow10 []uint64 - -func init() { - pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} -} - -// WriteFloat32 write float32 to stream -func (stream *Stream) WriteFloat32(val float32) { - if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { - stream.Error = fmt.Errorf("unsupported value: %f", val) - return - } - abs := math.Abs(float64(val)) - fmt := byte('f') - // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. - if abs != 0 { - if float32(abs) < 1e-6 || float32(abs) >= 1e21 { - fmt = 'e' - } - } - stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 32) -} - -// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster -func (stream *Stream) WriteFloat32Lossy(val float32) { - if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { - stream.Error = fmt.Errorf("unsupported value: %f", val) - return - } - if val < 0 { - stream.writeByte('-') - val = -val - } - if val > 0x4ffffff { - stream.WriteFloat32(val) - return - } - precision := 6 - exp := uint64(1000000) // 6 - lval := uint64(float64(val)*float64(exp) + 0.5) - stream.WriteUint64(lval / exp) - fval := lval % exp - if fval == 0 { - return - } - stream.writeByte('.') - for p := precision - 1; p > 0 && fval < pow10[p]; p-- { - stream.writeByte('0') - } - stream.WriteUint64(fval) - for stream.buf[len(stream.buf)-1] == '0' { - stream.buf = stream.buf[:len(stream.buf)-1] - } -} - -// WriteFloat64 write float64 to stream -func (stream *Stream) WriteFloat64(val float64) { - if math.IsInf(val, 0) || math.IsNaN(val) { - stream.Error = fmt.Errorf("unsupported value: %f", val) - return - } - abs := math.Abs(val) - fmt := byte('f') - // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. - if abs != 0 { - if abs < 1e-6 || abs >= 1e21 { - fmt = 'e' - } - } - stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 64) -} - -// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster -func (stream *Stream) WriteFloat64Lossy(val float64) { - if math.IsInf(val, 0) || math.IsNaN(val) { - stream.Error = fmt.Errorf("unsupported value: %f", val) - return - } - if val < 0 { - stream.writeByte('-') - val = -val - } - if val > 0x4ffffff { - stream.WriteFloat64(val) - return - } - precision := 6 - exp := uint64(1000000) // 6 - lval := uint64(val*float64(exp) + 0.5) - stream.WriteUint64(lval / exp) - fval := lval % exp - if fval == 0 { - return - } - stream.writeByte('.') - for p := precision - 1; p > 0 && fval < pow10[p]; p-- { - stream.writeByte('0') - } - stream.WriteUint64(fval) - for stream.buf[len(stream.buf)-1] == '0' { - stream.buf = stream.buf[:len(stream.buf)-1] - } -} diff --git a/vendor/github.com/json-iterator/go/stream_int.go b/vendor/github.com/json-iterator/go/stream_int.go deleted file mode 100644 index d1059ee4..00000000 --- a/vendor/github.com/json-iterator/go/stream_int.go +++ /dev/null @@ -1,190 +0,0 @@ -package jsoniter - -var digits []uint32 - -func init() { - digits = make([]uint32, 1000) - for i := uint32(0); i < 1000; i++ { - digits[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i%10 + '0' - if i < 10 { - digits[i] += 2 << 24 - } else if i < 100 { - digits[i] += 1 << 24 - } - } -} - -func writeFirstBuf(space []byte, v uint32) []byte { - start := v >> 24 - if start == 0 { - space = append(space, byte(v>>16), byte(v>>8)) - } else if start == 1 { - space = append(space, byte(v>>8)) - } - space = append(space, byte(v)) - return space -} - -func writeBuf(buf []byte, v uint32) []byte { - return append(buf, byte(v>>16), byte(v>>8), byte(v)) -} - -// WriteUint8 write uint8 to stream -func (stream *Stream) WriteUint8(val uint8) { - stream.buf = writeFirstBuf(stream.buf, digits[val]) -} - -// WriteInt8 write int8 to stream -func (stream *Stream) WriteInt8(nval int8) { - var val uint8 - if nval < 0 { - val = uint8(-nval) - stream.buf = append(stream.buf, '-') - } else { - val = uint8(nval) - } - stream.buf = writeFirstBuf(stream.buf, digits[val]) -} - -// WriteUint16 write uint16 to stream -func (stream *Stream) WriteUint16(val uint16) { - q1 := val / 1000 - if q1 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[val]) - return - } - r1 := val - q1*1000 - stream.buf = writeFirstBuf(stream.buf, digits[q1]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return -} - -// WriteInt16 write int16 to stream -func (stream *Stream) WriteInt16(nval int16) { - var val uint16 - if nval < 0 { - val = uint16(-nval) - stream.buf = append(stream.buf, '-') - } else { - val = uint16(nval) - } - stream.WriteUint16(val) -} - -// WriteUint32 write uint32 to stream -func (stream *Stream) WriteUint32(val uint32) { - q1 := val / 1000 - if q1 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[val]) - return - } - r1 := val - q1*1000 - q2 := q1 / 1000 - if q2 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q1]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return - } - r2 := q1 - q2*1000 - q3 := q2 / 1000 - if q3 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q2]) - } else { - r3 := q2 - q3*1000 - stream.buf = append(stream.buf, byte(q3+'0')) - stream.buf = writeBuf(stream.buf, digits[r3]) - } - stream.buf = writeBuf(stream.buf, digits[r2]) - stream.buf = writeBuf(stream.buf, digits[r1]) -} - -// WriteInt32 write int32 to stream -func (stream *Stream) WriteInt32(nval int32) { - var val uint32 - if nval < 0 { - val = uint32(-nval) - stream.buf = append(stream.buf, '-') - } else { - val = uint32(nval) - } - stream.WriteUint32(val) -} - -// WriteUint64 write uint64 to stream -func (stream *Stream) WriteUint64(val uint64) { - q1 := val / 1000 - if q1 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[val]) - return - } - r1 := val - q1*1000 - q2 := q1 / 1000 - if q2 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q1]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return - } - r2 := q1 - q2*1000 - q3 := q2 / 1000 - if q3 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q2]) - stream.buf = writeBuf(stream.buf, digits[r2]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return - } - r3 := q2 - q3*1000 - q4 := q3 / 1000 - if q4 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q3]) - stream.buf = writeBuf(stream.buf, digits[r3]) - stream.buf = writeBuf(stream.buf, digits[r2]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return - } - r4 := q3 - q4*1000 - q5 := q4 / 1000 - if q5 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q4]) - stream.buf = writeBuf(stream.buf, digits[r4]) - stream.buf = writeBuf(stream.buf, digits[r3]) - stream.buf = writeBuf(stream.buf, digits[r2]) - stream.buf = writeBuf(stream.buf, digits[r1]) - return - } - r5 := q4 - q5*1000 - q6 := q5 / 1000 - if q6 == 0 { - stream.buf = writeFirstBuf(stream.buf, digits[q5]) - } else { - stream.buf = writeFirstBuf(stream.buf, digits[q6]) - r6 := q5 - q6*1000 - stream.buf = writeBuf(stream.buf, digits[r6]) - } - stream.buf = writeBuf(stream.buf, digits[r5]) - stream.buf = writeBuf(stream.buf, digits[r4]) - stream.buf = writeBuf(stream.buf, digits[r3]) - stream.buf = writeBuf(stream.buf, digits[r2]) - stream.buf = writeBuf(stream.buf, digits[r1]) -} - -// WriteInt64 write int64 to stream -func (stream *Stream) WriteInt64(nval int64) { - var val uint64 - if nval < 0 { - val = uint64(-nval) - stream.buf = append(stream.buf, '-') - } else { - val = uint64(nval) - } - stream.WriteUint64(val) -} - -// WriteInt write int to stream -func (stream *Stream) WriteInt(val int) { - stream.WriteInt64(int64(val)) -} - -// WriteUint write uint to stream -func (stream *Stream) WriteUint(val uint) { - stream.WriteUint64(uint64(val)) -} diff --git a/vendor/github.com/json-iterator/go/stream_str.go b/vendor/github.com/json-iterator/go/stream_str.go deleted file mode 100644 index 54c2ba0b..00000000 --- a/vendor/github.com/json-iterator/go/stream_str.go +++ /dev/null @@ -1,372 +0,0 @@ -package jsoniter - -import ( - "unicode/utf8" -) - -// htmlSafeSet holds the value true if the ASCII character with the given -// array position can be safely represented inside a JSON string, embedded -// inside of HTML