mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-03-10 01:19:29 +00:00
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
482 lines
12 KiB
Go
482 lines
12 KiB
Go
// Copyright 2015 go-swagger maintainers
|
|
//
|
|
// 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 swag
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/mailru/easyjson/jlexer"
|
|
"github.com/mailru/easyjson/jwriter"
|
|
yaml "gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// YAMLMatcher matches yaml
|
|
func YAMLMatcher(path string) bool {
|
|
ext := filepath.Ext(path)
|
|
return ext == ".yaml" || ext == ".yml"
|
|
}
|
|
|
|
// YAMLToJSON converts YAML unmarshaled data into json compatible data
|
|
func YAMLToJSON(data interface{}) (json.RawMessage, error) {
|
|
jm, err := transformData(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := WriteJSON(jm)
|
|
return json.RawMessage(b), err
|
|
}
|
|
|
|
// BytesToYAMLDoc converts a byte slice into a YAML document
|
|
func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
|
var document yaml.Node // preserve order that is present in the document
|
|
if err := yaml.Unmarshal(data, &document); err != nil {
|
|
return nil, err
|
|
}
|
|
if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
|
|
return nil, errors.New("only YAML documents that are objects are supported")
|
|
}
|
|
return &document, nil
|
|
}
|
|
|
|
func yamlNode(root *yaml.Node) (interface{}, error) {
|
|
switch root.Kind {
|
|
case yaml.DocumentNode:
|
|
return yamlDocument(root)
|
|
case yaml.SequenceNode:
|
|
return yamlSequence(root)
|
|
case yaml.MappingNode:
|
|
return yamlMapping(root)
|
|
case yaml.ScalarNode:
|
|
return yamlScalar(root)
|
|
case yaml.AliasNode:
|
|
return yamlNode(root.Alias)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind)
|
|
}
|
|
}
|
|
|
|
func yamlDocument(node *yaml.Node) (interface{}, error) {
|
|
if len(node.Content) != 1 {
|
|
return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content))
|
|
}
|
|
return yamlNode(node.Content[0])
|
|
}
|
|
|
|
func yamlMapping(node *yaml.Node) (interface{}, error) {
|
|
m := make(JSONMapSlice, len(node.Content)/2)
|
|
|
|
var j int
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
var nmi JSONMapItem
|
|
k, err := yamlStringScalarC(node.Content[i])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to decode YAML map key: %w", err)
|
|
}
|
|
nmi.Key = k
|
|
v, err := yamlNode(node.Content[i+1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err)
|
|
}
|
|
nmi.Value = v
|
|
m[j] = nmi
|
|
j++
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func yamlSequence(node *yaml.Node) (interface{}, error) {
|
|
s := make([]interface{}, 0)
|
|
|
|
for i := 0; i < len(node.Content); i++ {
|
|
|
|
v, err := yamlNode(node.Content[i])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err)
|
|
}
|
|
s = append(s, v)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
const ( // See https://yaml.org/type/
|
|
yamlStringScalar = "tag:yaml.org,2002:str"
|
|
yamlIntScalar = "tag:yaml.org,2002:int"
|
|
yamlBoolScalar = "tag:yaml.org,2002:bool"
|
|
yamlFloatScalar = "tag:yaml.org,2002:float"
|
|
yamlTimestamp = "tag:yaml.org,2002:timestamp"
|
|
yamlNull = "tag:yaml.org,2002:null"
|
|
)
|
|
|
|
func yamlScalar(node *yaml.Node) (interface{}, error) {
|
|
switch node.LongTag() {
|
|
case yamlStringScalar:
|
|
return node.Value, nil
|
|
case yamlBoolScalar:
|
|
b, err := strconv.ParseBool(node.Value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err)
|
|
}
|
|
return b, nil
|
|
case yamlIntScalar:
|
|
i, err := strconv.ParseInt(node.Value, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err)
|
|
}
|
|
return i, nil
|
|
case yamlFloatScalar:
|
|
f, err := strconv.ParseFloat(node.Value, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err)
|
|
}
|
|
return f, nil
|
|
case yamlTimestamp:
|
|
return node.Value, nil
|
|
case yamlNull:
|
|
return nil, nil //nolint:nilnil
|
|
default:
|
|
return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag())
|
|
}
|
|
}
|
|
|
|
func yamlStringScalarC(node *yaml.Node) (string, error) {
|
|
if node.Kind != yaml.ScalarNode {
|
|
return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind)
|
|
}
|
|
switch node.LongTag() {
|
|
case yamlStringScalar, yamlIntScalar, yamlFloatScalar:
|
|
return node.Value, nil
|
|
default:
|
|
return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag())
|
|
}
|
|
}
|
|
|
|
// JSONMapSlice represent a JSON object, with the order of keys maintained
|
|
type JSONMapSlice []JSONMapItem
|
|
|
|
// MarshalJSON renders a JSONMapSlice as JSON
|
|
func (s JSONMapSlice) MarshalJSON() ([]byte, error) {
|
|
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
|
s.MarshalEasyJSON(w)
|
|
return w.BuildBytes()
|
|
}
|
|
|
|
// MarshalEasyJSON renders a JSONMapSlice as JSON, using easyJSON
|
|
func (s JSONMapSlice) MarshalEasyJSON(w *jwriter.Writer) {
|
|
w.RawByte('{')
|
|
|
|
ln := len(s)
|
|
last := ln - 1
|
|
for i := 0; i < ln; i++ {
|
|
s[i].MarshalEasyJSON(w)
|
|
if i != last { // last item
|
|
w.RawByte(',')
|
|
}
|
|
}
|
|
|
|
w.RawByte('}')
|
|
}
|
|
|
|
// UnmarshalJSON makes a JSONMapSlice from JSON
|
|
func (s *JSONMapSlice) UnmarshalJSON(data []byte) error {
|
|
l := jlexer.Lexer{Data: data}
|
|
s.UnmarshalEasyJSON(&l)
|
|
return l.Error()
|
|
}
|
|
|
|
// UnmarshalEasyJSON makes a JSONMapSlice from JSON, using easyJSON
|
|
func (s *JSONMapSlice) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|
if in.IsNull() {
|
|
in.Skip()
|
|
return
|
|
}
|
|
|
|
var result JSONMapSlice
|
|
in.Delim('{')
|
|
for !in.IsDelim('}') {
|
|
var mi JSONMapItem
|
|
mi.UnmarshalEasyJSON(in)
|
|
result = append(result, mi)
|
|
}
|
|
*s = result
|
|
}
|
|
|
|
func (s JSONMapSlice) MarshalYAML() (interface{}, error) {
|
|
var n yaml.Node
|
|
n.Kind = yaml.DocumentNode
|
|
var nodes []*yaml.Node
|
|
for _, item := range s {
|
|
nn, err := json2yaml(item.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ns := []*yaml.Node{
|
|
{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlStringScalar,
|
|
Value: item.Key,
|
|
},
|
|
nn,
|
|
}
|
|
nodes = append(nodes, ns...)
|
|
}
|
|
|
|
n.Content = []*yaml.Node{
|
|
{
|
|
Kind: yaml.MappingNode,
|
|
Content: nodes,
|
|
},
|
|
}
|
|
|
|
return yaml.Marshal(&n)
|
|
}
|
|
|
|
func isNil(input interface{}) bool {
|
|
if input == nil {
|
|
return true
|
|
}
|
|
kind := reflect.TypeOf(input).Kind()
|
|
switch kind { //nolint:exhaustive
|
|
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
|
return reflect.ValueOf(input).IsNil()
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func json2yaml(item interface{}) (*yaml.Node, error) {
|
|
if isNil(item) {
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Value: "null",
|
|
}, nil
|
|
}
|
|
|
|
switch val := item.(type) {
|
|
case JSONMapSlice:
|
|
var n yaml.Node
|
|
n.Kind = yaml.MappingNode
|
|
for i := range val {
|
|
childNode, err := json2yaml(&val[i].Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Content = append(n.Content, &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlStringScalar,
|
|
Value: val[i].Key,
|
|
}, childNode)
|
|
}
|
|
return &n, nil
|
|
case map[string]interface{}:
|
|
var n yaml.Node
|
|
n.Kind = yaml.MappingNode
|
|
keys := make([]string, 0, len(val))
|
|
for k := range val {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
for _, k := range keys {
|
|
v := val[k]
|
|
childNode, err := json2yaml(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Content = append(n.Content, &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlStringScalar,
|
|
Value: k,
|
|
}, childNode)
|
|
}
|
|
return &n, nil
|
|
case []interface{}:
|
|
var n yaml.Node
|
|
n.Kind = yaml.SequenceNode
|
|
for i := range val {
|
|
childNode, err := json2yaml(val[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n.Content = append(n.Content, childNode)
|
|
}
|
|
return &n, nil
|
|
case string:
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlStringScalar,
|
|
Value: val,
|
|
}, nil
|
|
case float64:
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlFloatScalar,
|
|
Value: strconv.FormatFloat(val, 'f', -1, 64),
|
|
}, nil
|
|
case int64:
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlIntScalar,
|
|
Value: strconv.FormatInt(val, 10),
|
|
}, nil
|
|
case uint64:
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlIntScalar,
|
|
Value: strconv.FormatUint(val, 10),
|
|
}, nil
|
|
case bool:
|
|
return &yaml.Node{
|
|
Kind: yaml.ScalarNode,
|
|
Tag: yamlBoolScalar,
|
|
Value: strconv.FormatBool(val),
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unhandled type: %T", val)
|
|
}
|
|
}
|
|
|
|
// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
|
|
type JSONMapItem struct {
|
|
Key string
|
|
Value interface{}
|
|
}
|
|
|
|
// MarshalJSON renders a JSONMapItem as JSON
|
|
func (s JSONMapItem) MarshalJSON() ([]byte, error) {
|
|
w := &jwriter.Writer{Flags: jwriter.NilMapAsEmpty | jwriter.NilSliceAsEmpty}
|
|
s.MarshalEasyJSON(w)
|
|
return w.BuildBytes()
|
|
}
|
|
|
|
// MarshalEasyJSON renders a JSONMapItem as JSON, using easyJSON
|
|
func (s JSONMapItem) MarshalEasyJSON(w *jwriter.Writer) {
|
|
w.String(s.Key)
|
|
w.RawByte(':')
|
|
w.Raw(WriteJSON(s.Value))
|
|
}
|
|
|
|
// UnmarshalJSON makes a JSONMapItem from JSON
|
|
func (s *JSONMapItem) UnmarshalJSON(data []byte) error {
|
|
l := jlexer.Lexer{Data: data}
|
|
s.UnmarshalEasyJSON(&l)
|
|
return l.Error()
|
|
}
|
|
|
|
// UnmarshalEasyJSON makes a JSONMapItem from JSON, using easyJSON
|
|
func (s *JSONMapItem) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|
key := in.UnsafeString()
|
|
in.WantColon()
|
|
value := in.Interface()
|
|
in.WantComma()
|
|
s.Key = key
|
|
s.Value = value
|
|
}
|
|
|
|
func transformData(input interface{}) (out interface{}, err error) {
|
|
format := func(t interface{}) (string, error) {
|
|
switch k := t.(type) {
|
|
case string:
|
|
return k, nil
|
|
case uint:
|
|
return strconv.FormatUint(uint64(k), 10), nil
|
|
case uint8:
|
|
return strconv.FormatUint(uint64(k), 10), nil
|
|
case uint16:
|
|
return strconv.FormatUint(uint64(k), 10), nil
|
|
case uint32:
|
|
return strconv.FormatUint(uint64(k), 10), nil
|
|
case uint64:
|
|
return strconv.FormatUint(k, 10), nil
|
|
case int:
|
|
return strconv.Itoa(k), nil
|
|
case int8:
|
|
return strconv.FormatInt(int64(k), 10), nil
|
|
case int16:
|
|
return strconv.FormatInt(int64(k), 10), nil
|
|
case int32:
|
|
return strconv.FormatInt(int64(k), 10), nil
|
|
case int64:
|
|
return strconv.FormatInt(k, 10), nil
|
|
default:
|
|
return "", fmt.Errorf("unexpected map key type, got: %T", k)
|
|
}
|
|
}
|
|
|
|
switch in := input.(type) {
|
|
case yaml.Node:
|
|
return yamlNode(&in)
|
|
case *yaml.Node:
|
|
return yamlNode(in)
|
|
case map[interface{}]interface{}:
|
|
o := make(JSONMapSlice, 0, len(in))
|
|
for ke, va := range in {
|
|
var nmi JSONMapItem
|
|
if nmi.Key, err = format(ke); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v, ert := transformData(va)
|
|
if ert != nil {
|
|
return nil, ert
|
|
}
|
|
nmi.Value = v
|
|
o = append(o, nmi)
|
|
}
|
|
return o, nil
|
|
case []interface{}:
|
|
len1 := len(in)
|
|
o := make([]interface{}, len1)
|
|
for i := 0; i < len1; i++ {
|
|
o[i], err = transformData(in[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return o, nil
|
|
}
|
|
return input, nil
|
|
}
|
|
|
|
// YAMLDoc loads a yaml document from either http or a file and converts it to json
|
|
func YAMLDoc(path string) (json.RawMessage, error) {
|
|
yamlDoc, err := YAMLData(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := YAMLToJSON(yamlDoc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// YAMLData loads a yaml document from either http or a file
|
|
func YAMLData(path string) (interface{}, error) {
|
|
data, err := LoadFromFileOrHTTP(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return BytesToYAMLDoc(data)
|
|
}
|