rbd: add aws-sts-metdata encryption type

With Amazon STS and kubernetes cluster is configured with
OIDC identity provider, credentials to access Amazon KMS
can be fetched using oidc-token(serviceaccount token).
Each tenant/namespace needs to create a secret with aws region,
role and CMK ARN.
Ceph-CSI will assume the given role with oidc token and access
aws KMS, with given CMK to encrypt/decrypt DEK which will stored
in the image metdata.

Refer: https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html
Resolves: #2879

Signed-off-by: Rakshith R <rar@redhat.com>
This commit is contained in:
Rakshith R
2022-03-02 16:00:48 +05:30
committed by mergify[bot]
parent 13dcc89ac8
commit 4f0bb2315b
217 changed files with 24757 additions and 72 deletions

49
vendor/github.com/aws/smithy-go/encoding/xml/array.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
package xml
// arrayMemberWrapper is the default member wrapper tag name for XML Array type
var arrayMemberWrapper = StartElement{
Name: Name{Local: "member"},
}
// Array represents the encoding of a XML array type
type Array struct {
w writer
scratch *[]byte
// member start element is the array member wrapper start element
memberStartElement StartElement
// isFlattened indicates if the array is a flattened array.
isFlattened bool
}
// newArray returns an array encoder.
// It also takes in the member start element, array start element.
// It takes in a isFlattened bool, indicating that an array is flattened array.
//
// A wrapped array ["value1", "value2"] is represented as
// `<List><member>value1</member><member>value2</member></List>`.
// A flattened array `someList: ["value1", "value2"]` is represented as
// `<someList>value1</someList><someList>value2</someList>`.
func newArray(w writer, scratch *[]byte, memberStartElement StartElement, arrayStartElement StartElement, isFlattened bool) *Array {
var memberWrapper = memberStartElement
if isFlattened {
memberWrapper = arrayStartElement
}
return &Array{
w: w,
scratch: scratch,
memberStartElement: memberWrapper,
isFlattened: isFlattened,
}
}
// Member adds a new member to the XML array.
// It returns a Value encoder.
func (a *Array) Member() Value {
v := newValue(a.w, a.scratch, a.memberStartElement)
v.isFlattened = a.isFlattened
return v
}

View File

@ -0,0 +1,10 @@
package xml
const (
leftAngleBracket = '<'
rightAngleBracket = '>'
forwardSlash = '/'
colon = ':'
equals = '='
quote = '"'
)

49
vendor/github.com/aws/smithy-go/encoding/xml/doc.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
/*
Package xml holds the XMl encoder utility. This utility is written in accordance to our design to delegate to
shape serializer function in which a xml.Value will be passed around.
Resources followed: https://awslabs.github.io/smithy/1.0/spec/core/xml-traits.html#
Member Element
Member element should be used to encode xml shapes into xml elements except for flattened xml shapes. Member element
write their own element start tag. These elements should always be closed.
Flattened Element
Flattened element should be used to encode shapes marked with flattened trait into xml elements. Flattened element
do not write a start tag, and thus should not be closed.
Simple types encoding
All simple type methods on value such as String(), Long() etc; auto close the associated member element.
Array
Array returns the collection encoder. It has two modes, wrapped and flattened encoding.
Wrapped arrays have two methods Array() and ArrayWithCustomName() which facilitate array member wrapping.
By default, a wrapped array members are wrapped with `member` named start element.
<wrappedArray><member>apple</member><member>tree</member></wrappedArray>
Flattened arrays rely on Value being marked as flattened.
If a shape is marked as flattened, Array() will use the shape element name as wrapper for array elements.
<flattenedAarray>apple</flattenedArray><flattenedArray>tree</flattenedArray>
Map
Map is the map encoder. It has two modes, wrapped and flattened encoding.
Wrapped map has Array() method, which facilitate map member wrapping.
By default, a wrapped map members are wrapped with `entry` named start element.
<wrappedMap><entry><Key>apple</Key><Value>tree</Value></entry><entry><Key>snow</Key><Value>ice</Value></entry></wrappedMap>
Flattened map rely on Value being marked as flattened.
If a shape is marked as flattened, Map() will use the shape element name as wrapper for map entry elements.
<flattenedMap><Key>apple</Key><Value>tree</Value></flattenedMap><flattenedMap><Key>snow</Key><Value>ice</Value></flattenedMap>
*/
package xml

View File

@ -0,0 +1,91 @@
// Copyright 2009 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.
// Copied and modified from Go 1.14 stdlib's encoding/xml
package xml
// A Name represents an XML name (Local) annotated
// with a name space identifier (Space).
// In tokens returned by Decoder.Token, the Space identifier
// is given as a canonical URL, not the short prefix used
// in the document being parsed.
type Name struct {
Space, Local string
}
// An Attr represents an attribute in an XML element (Name=Value).
type Attr struct {
Name Name
Value string
}
/*
NewAttribute returns a pointer to an attribute.
It takes in a local name aka attribute name, and value
representing the attribute value.
*/
func NewAttribute(local, value string) Attr {
return Attr{
Name: Name{
Local: local,
},
Value: value,
}
}
/*
NewNamespaceAttribute returns a pointer to an attribute.
It takes in a local name aka attribute name, and value
representing the attribute value.
NewNamespaceAttribute appends `xmlns:` in front of namespace
prefix.
For creating a name space attribute representing
`xmlns:prefix="http://example.com`, the breakdown would be:
local = "prefix"
value = "http://example.com"
*/
func NewNamespaceAttribute(local, value string) Attr {
attr := NewAttribute(local, value)
// default name space identifier
attr.Name.Space = "xmlns"
return attr
}
// A StartElement represents an XML start element.
type StartElement struct {
Name Name
Attr []Attr
}
// Copy creates a new copy of StartElement.
func (e StartElement) Copy() StartElement {
attrs := make([]Attr, len(e.Attr))
copy(attrs, e.Attr)
e.Attr = attrs
return e
}
// End returns the corresponding XML end element.
func (e StartElement) End() EndElement {
return EndElement{e.Name}
}
// returns true if start element local name is empty
func (e StartElement) isZero() bool {
return len(e.Name.Local) == 0
}
// An EndElement represents an XML end element.
type EndElement struct {
Name Name
}
// returns true if end element local name is empty
func (e EndElement) isZero() bool {
return len(e.Name.Local) == 0
}

View File

@ -0,0 +1,51 @@
package xml
// writer interface used by the xml encoder to write an encoded xml
// document in a writer.
type writer interface {
// Write takes in a byte slice and returns number of bytes written and error
Write(p []byte) (n int, err error)
// WriteRune takes in a rune and returns number of bytes written and error
WriteRune(r rune) (n int, err error)
// WriteString takes in a string and returns number of bytes written and error
WriteString(s string) (n int, err error)
// String method returns a string
String() string
// Bytes return a byte slice.
Bytes() []byte
}
// Encoder is an XML encoder that supports construction of XML values
// using methods. The encoder takes in a writer and maintains a scratch buffer.
type Encoder struct {
w writer
scratch *[]byte
}
// NewEncoder returns an XML encoder
func NewEncoder(w writer) *Encoder {
scratch := make([]byte, 64)
return &Encoder{w: w, scratch: &scratch}
}
// String returns the string output of the XML encoder
func (e Encoder) String() string {
return e.w.String()
}
// Bytes returns the []byte slice of the XML encoder
func (e Encoder) Bytes() []byte {
return e.w.Bytes()
}
// RootElement builds a root element encoding
// It writes it's start element tag. The value should be closed.
func (e Encoder) RootElement(element StartElement) Value {
return newValue(e.w, e.scratch, element)
}

View File

@ -0,0 +1,51 @@
package xml
import (
"encoding/xml"
"fmt"
"io"
)
// ErrorComponents represents the error response fields
// that will be deserialized from an xml error response body
type ErrorComponents struct {
Code string
Message string
}
// GetErrorResponseComponents returns the error fields from an xml error response body
func GetErrorResponseComponents(r io.Reader, noErrorWrapping bool) (ErrorComponents, error) {
if noErrorWrapping {
var errResponse noWrappedErrorResponse
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
}
return ErrorComponents{
Code: errResponse.Code,
Message: errResponse.Message,
}, nil
}
var errResponse wrappedErrorResponse
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
}
return ErrorComponents{
Code: errResponse.Code,
Message: errResponse.Message,
}, nil
}
// noWrappedErrorResponse represents the error response body with
// no internal <Error></Error wrapping
type noWrappedErrorResponse struct {
Code string `xml:"Code"`
Message string `xml:"Message"`
}
// wrappedErrorResponse represents the error response body
// wrapped within <Error>...</Error>
type wrappedErrorResponse struct {
Code string `xml:"Error>Code"`
Message string `xml:"Error>Message"`
}

137
vendor/github.com/aws/smithy-go/encoding/xml/escape.go generated vendored Normal file
View File

@ -0,0 +1,137 @@
// Copyright 2009 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.
// Copied and modified from Go 1.14 stdlib's encoding/xml
package xml
import (
"unicode/utf8"
)
// Copied from Go 1.14 stdlib's encoding/xml
var (
escQuot = []byte("&#34;") // shorter than "&quot;"
escApos = []byte("&#39;") // shorter than "&apos;"
escAmp = []byte("&amp;")
escLT = []byte("&lt;")
escGT = []byte("&gt;")
escTab = []byte("&#x9;")
escNL = []byte("&#xA;")
escCR = []byte("&#xD;")
escFFFD = []byte("\uFFFD") // Unicode replacement character
// Additional Escapes
escNextLine = []byte("&#x85;")
escLS = []byte("&#x2028;")
)
// Decide whether the given rune is in the XML Character Range, per
// the Char production of https://www.xml.com/axml/testaxml.htm,
// Section 2.2 Characters.
func isInCharacterRange(r rune) (inrange bool) {
return r == 0x09 ||
r == 0x0A ||
r == 0x0D ||
r >= 0x20 && r <= 0xD7FF ||
r >= 0xE000 && r <= 0xFFFD ||
r >= 0x10000 && r <= 0x10FFFF
}
// TODO: When do we need to escape the string?
// Based on encoding/xml escapeString from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeString(e writer, s string) {
var esc []byte
last := 0
for i := 0; i < len(s); {
r, width := utf8.DecodeRuneInString(s[i:])
i += width
switch r {
case '"':
esc = escQuot
case '\'':
esc = escApos
case '&':
esc = escAmp
case '<':
esc = escLT
case '>':
esc = escGT
case '\t':
esc = escTab
case '\n':
esc = escNL
case '\r':
esc = escCR
case '\u0085':
// Not escaped by stdlib
esc = escNextLine
case '\u2028':
// Not escaped by stdlib
esc = escLS
default:
if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = escFFFD
break
}
continue
}
e.WriteString(s[last : i-width])
e.Write(esc)
last = i
}
e.WriteString(s[last:])
}
// escapeText writes to w the properly escaped XML equivalent
// of the plain text data s. If escapeNewline is true, newline
// characters will be escaped.
//
// Based on encoding/xml escapeText from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeText(e writer, s []byte) {
var esc []byte
last := 0
for i := 0; i < len(s); {
r, width := utf8.DecodeRune(s[i:])
i += width
switch r {
case '"':
esc = escQuot
case '\'':
esc = escApos
case '&':
esc = escAmp
case '<':
esc = escLT
case '>':
esc = escGT
case '\t':
esc = escTab
case '\n':
// This always escapes newline, which is different than stdlib's optional
// escape of new line.
esc = escNL
case '\r':
esc = escCR
case '\u0085':
// Not escaped by stdlib
esc = escNextLine
case '\u2028':
// Not escaped by stdlib
esc = escLS
default:
if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = escFFFD
break
}
continue
}
e.Write(s[last : i-width])
e.Write(esc)
last = i
}
e.Write(s[last:])
}

53
vendor/github.com/aws/smithy-go/encoding/xml/map.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package xml
// mapEntryWrapper is the default member wrapper start element for XML Map entry
var mapEntryWrapper = StartElement{
Name: Name{Local: "entry"},
}
// Map represents the encoding of a XML map type
type Map struct {
w writer
scratch *[]byte
// member start element is the map entry wrapper start element
memberStartElement StartElement
// isFlattened returns true if the map is a flattened map
isFlattened bool
}
// newMap returns a map encoder which sets the default map
// entry wrapper to `entry`.
//
// A map `someMap : {{key:"abc", value:"123"}}` is represented as
// `<someMap><entry><key>abc<key><value>123</value></entry></someMap>`.
func newMap(w writer, scratch *[]byte) *Map {
return &Map{
w: w,
scratch: scratch,
memberStartElement: mapEntryWrapper,
}
}
// newFlattenedMap returns a map encoder which sets the map
// entry wrapper to the passed in memberWrapper`.
//
// A flattened map `someMap : {{key:"abc", value:"123"}}` is represented as
// `<someMap><key>abc<key><value>123</value></someMap>`.
func newFlattenedMap(w writer, scratch *[]byte, memberWrapper StartElement) *Map {
return &Map{
w: w,
scratch: scratch,
memberStartElement: memberWrapper,
isFlattened: true,
}
}
// Entry returns a Value encoder with map's element.
// It writes the member wrapper start tag for each entry.
func (m *Map) Entry() Value {
v := newValue(m.w, m.scratch, m.memberStartElement)
v.isFlattened = m.isFlattened
return v
}

302
vendor/github.com/aws/smithy-go/encoding/xml/value.go generated vendored Normal file
View File

@ -0,0 +1,302 @@
package xml
import (
"encoding/base64"
"fmt"
"math/big"
"strconv"
"github.com/aws/smithy-go/encoding"
)
// Value represents an XML Value type
// XML Value types: Object, Array, Map, String, Number, Boolean.
type Value struct {
w writer
scratch *[]byte
// xml start element is the associated start element for the Value
startElement StartElement
// indicates if the Value represents a flattened shape
isFlattened bool
}
// newFlattenedValue returns a Value encoder. newFlattenedValue does NOT write the start element tag
func newFlattenedValue(w writer, scratch *[]byte, startElement StartElement) Value {
return Value{
w: w,
scratch: scratch,
startElement: startElement,
}
}
// newValue writes the start element xml tag and returns a Value
func newValue(w writer, scratch *[]byte, startElement StartElement) Value {
writeStartElement(w, startElement)
return Value{w: w, scratch: scratch, startElement: startElement}
}
// writeStartElement takes in a start element and writes it.
// It handles namespace, attributes in start element.
func writeStartElement(w writer, el StartElement) error {
if el.isZero() {
return fmt.Errorf("xml start element cannot be nil")
}
w.WriteRune(leftAngleBracket)
if len(el.Name.Space) != 0 {
escapeString(w, el.Name.Space)
w.WriteRune(colon)
}
escapeString(w, el.Name.Local)
for _, attr := range el.Attr {
w.WriteRune(' ')
writeAttribute(w, &attr)
}
w.WriteRune(rightAngleBracket)
return nil
}
// writeAttribute writes an attribute from a provided Attribute
// For a namespace attribute, the attr.Name.Space must be defined as "xmlns".
// https://www.w3.org/TR/REC-xml-names/#NT-DefaultAttName
func writeAttribute(w writer, attr *Attr) {
// if local, space both are not empty
if len(attr.Name.Space) != 0 && len(attr.Name.Local) != 0 {
escapeString(w, attr.Name.Space)
w.WriteRune(colon)
}
// if prefix is empty, the default `xmlns` space should be used as prefix.
if len(attr.Name.Local) == 0 {
attr.Name.Local = attr.Name.Space
}
escapeString(w, attr.Name.Local)
w.WriteRune(equals)
w.WriteRune(quote)
escapeString(w, attr.Value)
w.WriteRune(quote)
}
// writeEndElement takes in a end element and writes it.
func writeEndElement(w writer, el EndElement) error {
if el.isZero() {
return fmt.Errorf("xml end element cannot be nil")
}
w.WriteRune(leftAngleBracket)
w.WriteRune(forwardSlash)
if len(el.Name.Space) != 0 {
escapeString(w, el.Name.Space)
w.WriteRune(colon)
}
escapeString(w, el.Name.Local)
w.WriteRune(rightAngleBracket)
return nil
}
// String encodes v as a XML string.
// It will auto close the parent xml element tag.
func (xv Value) String(v string) {
escapeString(xv.w, v)
xv.Close()
}
// Byte encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Byte(v int8) {
xv.Long(int64(v))
}
// Short encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Short(v int16) {
xv.Long(int64(v))
}
// Integer encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Integer(v int32) {
xv.Long(int64(v))
}
// Long encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Long(v int64) {
*xv.scratch = strconv.AppendInt((*xv.scratch)[:0], v, 10)
xv.w.Write(*xv.scratch)
xv.Close()
}
// Float encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Float(v float32) {
xv.float(float64(v), 32)
xv.Close()
}
// Double encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Double(v float64) {
xv.float(v, 64)
xv.Close()
}
func (xv Value) float(v float64, bits int) {
*xv.scratch = encoding.EncodeFloat((*xv.scratch)[:0], v, bits)
xv.w.Write(*xv.scratch)
}
// Boolean encodes v as a XML boolean.
// It will auto close the parent xml element tag.
func (xv Value) Boolean(v bool) {
*xv.scratch = strconv.AppendBool((*xv.scratch)[:0], v)
xv.w.Write(*xv.scratch)
xv.Close()
}
// Base64EncodeBytes writes v as a base64 value in XML string.
// It will auto close the parent xml element tag.
func (xv Value) Base64EncodeBytes(v []byte) {
encodeByteSlice(xv.w, (*xv.scratch)[:0], v)
xv.Close()
}
// BigInteger encodes v big.Int as XML value.
// It will auto close the parent xml element tag.
func (xv Value) BigInteger(v *big.Int) {
xv.w.Write([]byte(v.Text(10)))
xv.Close()
}
// BigDecimal encodes v big.Float as XML value.
// It will auto close the parent xml element tag.
func (xv Value) BigDecimal(v *big.Float) {
if i, accuracy := v.Int64(); accuracy == big.Exact {
xv.Long(i)
return
}
xv.w.Write([]byte(v.Text('e', -1)))
xv.Close()
}
// Write writes v directly to the xml document
// if escapeXMLText is set to true, write will escape text.
// It will auto close the parent xml element tag.
func (xv Value) Write(v []byte, escapeXMLText bool) {
// escape and write xml text
if escapeXMLText {
escapeText(xv.w, v)
} else {
// write xml directly
xv.w.Write(v)
}
xv.Close()
}
// MemberElement does member element encoding. It returns a Value.
// Member Element method should be used for all shapes except flattened shapes.
//
// A call to MemberElement will write nested element tags directly using the
// provided start element. The value returned by MemberElement should be closed.
func (xv Value) MemberElement(element StartElement) Value {
return newValue(xv.w, xv.scratch, element)
}
// FlattenedElement returns flattened element encoding. It returns a Value.
// This method should be used for flattened shapes.
//
// Unlike MemberElement, flattened element will NOT write element tags
// directly for the associated start element.
//
// The value returned by the FlattenedElement does not need to be closed.
func (xv Value) FlattenedElement(element StartElement) Value {
v := newFlattenedValue(xv.w, xv.scratch, element)
v.isFlattened = true
return v
}
// Array returns an array encoder. By default, the members of array are
// wrapped with `<member>` element tag.
// If value is marked as flattened, the start element is used to wrap the members instead of
// the `<member>` element.
func (xv Value) Array() *Array {
return newArray(xv.w, xv.scratch, arrayMemberWrapper, xv.startElement, xv.isFlattened)
}
/*
ArrayWithCustomName returns an array encoder.
It takes named start element as an argument, the named start element will used to wrap xml array entries.
for eg, `<someList><customName>entry1</customName></someList>`
Here `customName` named start element will be wrapped on each array member.
*/
func (xv Value) ArrayWithCustomName(element StartElement) *Array {
return newArray(xv.w, xv.scratch, element, xv.startElement, xv.isFlattened)
}
/*
Map returns a map encoder. By default, the map entries are
wrapped with `<entry>` element tag.
If value is marked as flattened, the start element is used to wrap the entry instead of
the `<member>` element.
*/
func (xv Value) Map() *Map {
// flattened map
if xv.isFlattened {
return newFlattenedMap(xv.w, xv.scratch, xv.startElement)
}
// un-flattened map
return newMap(xv.w, xv.scratch)
}
// encodeByteSlice is modified copy of json encoder's encodeByteSlice.
// It is used to base64 encode a byte slice.
func encodeByteSlice(w writer, scratch []byte, v []byte) {
if v == nil {
return
}
encodedLen := base64.StdEncoding.EncodedLen(len(v))
if encodedLen <= len(scratch) {
// If the encoded bytes fit in e.scratch, avoid an extra
// allocation and use the cheaper Encoding.Encode.
dst := scratch[:encodedLen]
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else if encodedLen <= 1024 {
// The encoded bytes are short enough to allocate for, and
// Encoding.Encode is still cheaper.
dst := make([]byte, encodedLen)
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else {
// The encoded bytes are too long to cheaply allocate, and
// Encoding.Encode is no longer noticeably cheaper.
enc := base64.NewEncoder(base64.StdEncoding, w)
enc.Write(v)
enc.Close()
}
}
// IsFlattened returns true if value is for flattened shape.
func (xv Value) IsFlattened() bool {
return xv.isFlattened
}
// Close closes the value.
func (xv Value) Close() {
writeEndElement(xv.w, xv.startElement.End())
}

View File

@ -0,0 +1,154 @@
package xml
import (
"encoding/xml"
"fmt"
"strings"
)
// NodeDecoder is a XML decoder wrapper that is responsible to decoding
// a single XML Node element and it's nested member elements. This wrapper decoder
// takes in the start element of the top level node being decoded.
type NodeDecoder struct {
Decoder *xml.Decoder
StartEl xml.StartElement
}
// WrapNodeDecoder returns an initialized XMLNodeDecoder
func WrapNodeDecoder(decoder *xml.Decoder, startEl xml.StartElement) NodeDecoder {
return NodeDecoder{
Decoder: decoder,
StartEl: startEl,
}
}
// Token on a Node Decoder returns a xml StartElement. It returns a boolean that indicates the
// a token is the node decoder's end node token; and an error which indicates any error
// that occurred while retrieving the start element
func (d NodeDecoder) Token() (t xml.StartElement, done bool, err error) {
for {
token, e := d.Decoder.Token()
if e != nil {
return t, done, e
}
// check if we reach end of the node being decoded
if el, ok := token.(xml.EndElement); ok {
return t, el == d.StartEl.End(), err
}
if t, ok := token.(xml.StartElement); ok {
return restoreAttrNamespaces(t), false, err
}
// skip token if it is a comment or preamble or empty space value due to indentation
// or if it's a value and is not expected
}
}
// restoreAttrNamespaces update XML attributes to restore the short namespaces found within
// the raw XML document.
func restoreAttrNamespaces(node xml.StartElement) xml.StartElement {
if len(node.Attr) == 0 {
return node
}
// Generate a mapping of XML namespace values to their short names.
ns := map[string]string{}
for _, a := range node.Attr {
if a.Name.Space == "xmlns" {
ns[a.Value] = a.Name.Local
break
}
}
for i, a := range node.Attr {
if a.Name.Space == "xmlns" {
continue
}
// By default, xml.Decoder will fully resolve these namespaces. So if you had <foo xmlns:bar=baz bar:bin=hi/>
// then by default the second attribute would have the `Name.Space` resolved to `baz`. But we need it to
// continue to resolve as `bar` so we can easily identify it later on.
if v, ok := ns[node.Attr[i].Name.Space]; ok {
node.Attr[i].Name.Space = v
}
}
return node
}
// GetElement looks for the given tag name at the current level, and returns the element if found, and
// skipping over non-matching elements. Returns an error if the node is not found, or if an error occurs while walking
// the document.
func (d NodeDecoder) GetElement(name string) (t xml.StartElement, err error) {
for {
token, done, err := d.Token()
if err != nil {
return t, err
}
if done {
return t, fmt.Errorf("%s node not found", name)
}
switch {
case strings.EqualFold(name, token.Name.Local):
return token, nil
default:
err = d.Decoder.Skip()
if err != nil {
return t, err
}
}
}
}
// Value provides an abstraction to retrieve char data value within an xml element.
// The method will return an error if it encounters a nested xml element instead of char data.
// This method should only be used to retrieve simple type or blob shape values as []byte.
func (d NodeDecoder) Value() (c []byte, err error) {
t, e := d.Decoder.Token()
if e != nil {
return c, e
}
endElement := d.StartEl.End()
switch ev := t.(type) {
case xml.CharData:
c = ev.Copy()
case xml.EndElement: // end tag or self-closing
if ev == endElement {
return []byte{}, err
}
return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t)
default:
return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t)
}
t, e = d.Decoder.Token()
if e != nil {
return c, e
}
if ev, ok := t.(xml.EndElement); ok {
if ev == endElement {
return c, err
}
}
return c, fmt.Errorf("expected end element %v, got %T type %v instead", endElement, t, t)
}
// FetchRootElement takes in a decoder and returns the first start element within the xml body.
// This function is useful in fetching the start element of an XML response and ignore the
// comments and preamble
func FetchRootElement(decoder *xml.Decoder) (startElement xml.StartElement, err error) {
for {
t, e := decoder.Token()
if e != nil {
return startElement, e
}
if startElement, ok := t.(xml.StartElement); ok {
return startElement, err
}
}
}