mirror of
https://github.com/containers/podman.git
synced 2025-08-06 03:19:52 +08:00

Pull in updates made to the filters code for images. Filters now perform an AND operation except for th reference filter which does an OR operation for positive case but an AND operation for negative cases. Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
452 lines
11 KiB
Go
452 lines
11 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"
|
|
"fmt"
|
|
"path/filepath"
|
|
"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, fmt.Errorf("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 json2yaml(item interface{}) (*yaml.Node, error) {
|
|
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
|
|
for k, v := range val {
|
|
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
|
|
}
|
|
|
|
return nil, nil //nolint:nilnil
|
|
}
|
|
|
|
// 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)
|
|
}
|