2022-11-16 06:46:14 +00:00
|
|
|
// Copyright © 2022 Roberto Hidalgo <joao@un.rob.mx>
|
2022-12-31 06:14:08 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2022-11-16 06:46:14 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-12-20 05:49:37 +00:00
|
|
|
"sort"
|
2022-11-16 06:46:14 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
op "github.com/1Password/connect-sdk-go/onepassword"
|
2022-11-22 06:16:54 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2022-11-16 06:46:14 +00:00
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
)
|
|
|
|
|
2022-12-17 05:40:43 +00:00
|
|
|
// Entry is a configuration entry.
|
2022-12-18 18:16:46 +00:00
|
|
|
// Basically a copy of a yaml.Node with extra methods.
|
2022-11-16 06:46:14 +00:00
|
|
|
type Entry struct {
|
2022-11-22 06:16:54 +00:00
|
|
|
Value string
|
|
|
|
Kind yaml.Kind
|
|
|
|
Tag string
|
|
|
|
Path []string
|
|
|
|
Content []*Entry
|
|
|
|
Style yaml.Style
|
|
|
|
FootComment string
|
|
|
|
LineComment string
|
|
|
|
HeadComment string
|
|
|
|
Line int
|
|
|
|
Column int
|
2022-12-17 05:40:43 +00:00
|
|
|
// The ShortTag
|
|
|
|
Type string
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func NewEntry(name string, kind yaml.Kind) *Entry {
|
|
|
|
return &Entry{
|
|
|
|
Content: []*Entry{},
|
|
|
|
Value: name,
|
|
|
|
Kind: kind,
|
|
|
|
}
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 18:16:46 +00:00
|
|
|
func (e *Entry) copyFromNode(n *yaml.Node) {
|
2022-11-22 06:16:54 +00:00
|
|
|
if e.Content == nil {
|
|
|
|
e.Content = []*Entry{}
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
|
|
|
|
e.Kind = n.Kind
|
|
|
|
e.Value = n.Value
|
|
|
|
e.Tag = n.Tag
|
|
|
|
e.Style = n.Style
|
|
|
|
e.HeadComment = n.HeadComment
|
|
|
|
e.LineComment = n.LineComment
|
|
|
|
e.FootComment = n.FootComment
|
|
|
|
e.Line = n.Line
|
|
|
|
e.Column = n.Column
|
|
|
|
e.Type = n.ShortTag()
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) String() string {
|
2022-12-20 05:49:37 +00:00
|
|
|
if e.IsSecret() && yamlOutput.Has(OutputModeRedacted) {
|
|
|
|
return ""
|
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
return e.Value
|
|
|
|
}
|
|
|
|
|
2022-12-20 05:49:37 +00:00
|
|
|
func (e *Entry) Contents() []*Entry {
|
|
|
|
entries := []*Entry{}
|
|
|
|
|
|
|
|
if (e.Kind == yaml.MappingNode || e.Kind == yaml.DocumentNode) && yamlOutput.Has(OutputModeSorted) {
|
|
|
|
smes := []*sortedMapEntry{}
|
|
|
|
for i := 0; i < len(e.Content); i += 2 {
|
|
|
|
smes = append(smes, &sortedMapEntry{key: e.Content[i], value: e.Content[i+1]})
|
|
|
|
}
|
|
|
|
sortedEntries := &smec{smes}
|
|
|
|
sort.Sort(sortedEntries)
|
|
|
|
for _, v := range sortedEntries.smes {
|
|
|
|
entries = append(entries, v.key, v.value)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entries = e.Content
|
|
|
|
}
|
|
|
|
|
|
|
|
return entries
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) ChildNamed(name string) *Entry {
|
|
|
|
for _, child := range e.Content {
|
|
|
|
if child.Name() == name {
|
|
|
|
return child
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-11-16 06:46:14 +00:00
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) SetPath(parent []string, current string) {
|
2023-01-15 07:17:34 +00:00
|
|
|
if current != "." {
|
|
|
|
e.Path = append([]string{}, parent...)
|
|
|
|
e.Path = append(e.Path, current)
|
|
|
|
}
|
|
|
|
if e.IsScalar() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Kind == yaml.SequenceNode {
|
2022-11-22 06:16:54 +00:00
|
|
|
for idx, child := range e.Content {
|
2023-01-15 07:17:34 +00:00
|
|
|
child.SetPath(e.Path, fmt.Sprintf("%d", idx))
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2023-01-15 07:17:34 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx := 0; idx < len(e.Content); idx += 2 {
|
|
|
|
key := e.Content[idx]
|
|
|
|
child := e.Content[idx+1]
|
|
|
|
child.SetPath(e.Path, key.Value)
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) UnmarshalYAML(node *yaml.Node) error {
|
2022-12-18 18:16:46 +00:00
|
|
|
e.copyFromNode(node)
|
2022-11-22 06:16:54 +00:00
|
|
|
|
|
|
|
switch node.Kind {
|
|
|
|
case yaml.SequenceNode, yaml.ScalarNode:
|
|
|
|
for _, n := range node.Content {
|
|
|
|
sub := &Entry{}
|
2022-12-18 18:16:46 +00:00
|
|
|
sub.copyFromNode(n)
|
2022-11-22 06:16:54 +00:00
|
|
|
if err := n.Decode(&sub); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
e.Content = append(e.Content, sub)
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
case yaml.DocumentNode, yaml.MappingNode:
|
|
|
|
for idx := 0; idx < len(node.Content); idx += 2 {
|
|
|
|
keyNode := node.Content[idx]
|
|
|
|
valueNode := node.Content[idx+1]
|
|
|
|
key := NewEntry("", keyNode.Kind)
|
|
|
|
value := NewEntry(keyNode.Value, keyNode.Kind)
|
|
|
|
if err := keyNode.Decode(key); err != nil {
|
|
|
|
logrus.Errorf("decode map key: %s", keyNode.Value)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := valueNode.Decode(value); err != nil {
|
|
|
|
logrus.Errorf("decode map key: %s", keyNode.Value)
|
|
|
|
return err
|
|
|
|
}
|
2022-12-14 05:41:03 +00:00
|
|
|
if valueNode.Tag == YAMLTypeMetaConfig {
|
|
|
|
key.Type = YAMLTypeMetaConfig
|
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
e.Content = append(e.Content, key, value)
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown yaml type: %v", node.Kind)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-18 18:16:46 +00:00
|
|
|
func (e *Entry) IsScalar() bool {
|
|
|
|
return e.Kind != yaml.DocumentNode && e.Kind != yaml.MappingNode && e.Kind != yaml.SequenceNode
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) IsSecret() bool {
|
2022-12-14 05:41:03 +00:00
|
|
|
return e.Tag == YAMLTypeSecret
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) TypeStr() string {
|
|
|
|
if e.IsSecret() {
|
|
|
|
return "secret"
|
|
|
|
}
|
|
|
|
|
|
|
|
switch e.Type {
|
|
|
|
case "!!bool":
|
|
|
|
return "bool"
|
|
|
|
case "!!int":
|
|
|
|
return "int"
|
|
|
|
case "!!float":
|
|
|
|
return "float"
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) asNode() *yaml.Node {
|
2022-12-20 05:49:37 +00:00
|
|
|
n := &yaml.Node{
|
|
|
|
Kind: e.Kind,
|
|
|
|
Style: e.Style,
|
|
|
|
Tag: e.Tag,
|
|
|
|
Value: e.String(),
|
|
|
|
Line: e.Line,
|
|
|
|
Column: e.Column,
|
|
|
|
Content: []*yaml.Node{},
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
|
|
|
|
if !yamlOutput.Has(OutputModeNoComments) {
|
|
|
|
n.HeadComment = e.HeadComment
|
|
|
|
n.LineComment = e.LineComment
|
|
|
|
n.FootComment = e.FootComment
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) MarshalYAML() (*yaml.Node, error) {
|
|
|
|
n := e.asNode()
|
2022-12-20 05:49:37 +00:00
|
|
|
|
|
|
|
if e.Kind == yaml.SequenceNode {
|
|
|
|
for _, v := range e.Content {
|
|
|
|
node, err := v.MarshalYAML()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
n.Content = append(n.Content, node)
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
} else {
|
|
|
|
entries := e.Contents()
|
2023-01-11 04:50:06 +00:00
|
|
|
if len(entries)%2 != 0 {
|
|
|
|
return nil, fmt.Errorf("cannot decode odd numbered contents list: %s", e.Path)
|
|
|
|
}
|
|
|
|
|
2022-12-20 05:49:37 +00:00
|
|
|
for i := 0; i < len(entries); i += 2 {
|
|
|
|
key := entries[i]
|
|
|
|
value := entries[i+1]
|
|
|
|
if yamlOutput.Has(OutputModeNoConfig) && value.Type == YAMLTypeMetaConfig {
|
|
|
|
continue
|
|
|
|
}
|
2022-11-16 06:46:14 +00:00
|
|
|
|
2023-01-11 04:50:06 +00:00
|
|
|
if key.Type == "" {
|
|
|
|
key.Kind = yaml.ScalarNode
|
|
|
|
key.Type = "!!map"
|
|
|
|
}
|
|
|
|
|
2022-12-20 05:49:37 +00:00
|
|
|
keyNode, err := key.MarshalYAML()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
node, err := value.MarshalYAML()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
n.Content = append(n.Content, keyNode, node)
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
return n, nil
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) FromOP(fields []*op.ItemField) error {
|
|
|
|
annotations := map[string]string{}
|
|
|
|
data := map[string]string{}
|
2022-12-20 05:49:37 +00:00
|
|
|
entryKeys := []string{}
|
2022-11-16 06:46:14 +00:00
|
|
|
|
2022-12-18 18:16:46 +00:00
|
|
|
for _, field := range fields {
|
2022-11-16 06:46:14 +00:00
|
|
|
label := field.Label
|
|
|
|
if field.Section != nil {
|
|
|
|
if field.Section.Label == "~annotations" {
|
|
|
|
annotations[label] = field.Value
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
label = field.Section.Label + "." + label
|
|
|
|
}
|
|
|
|
}
|
2022-11-16 07:38:04 +00:00
|
|
|
if label == "password" || label == "notesPlain" {
|
|
|
|
continue
|
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
entryKeys = append(entryKeys, label)
|
2022-11-16 06:46:14 +00:00
|
|
|
data[label] = field.Value
|
|
|
|
}
|
|
|
|
|
2022-12-20 05:49:37 +00:00
|
|
|
for _, label := range entryKeys {
|
|
|
|
valueStr := data[label]
|
2022-11-22 06:16:54 +00:00
|
|
|
var style yaml.Style
|
|
|
|
var tag string
|
2023-01-10 07:02:10 +00:00
|
|
|
kind := ""
|
2022-11-22 06:16:54 +00:00
|
|
|
|
2022-12-18 18:16:46 +00:00
|
|
|
if annotations[label] == "secret" {
|
2022-11-22 06:16:54 +00:00
|
|
|
style = yaml.TaggedStyle
|
2022-12-14 05:41:03 +00:00
|
|
|
tag = YAMLTypeSecret
|
2023-01-10 07:02:10 +00:00
|
|
|
} else if k, ok := annotations[label]; ok {
|
|
|
|
kind = "!!" + k
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
path := strings.Split(label, ".")
|
|
|
|
container := e
|
|
|
|
|
|
|
|
for idx, key := range path {
|
|
|
|
if idx == len(path)-1 {
|
2022-12-20 05:49:37 +00:00
|
|
|
if existing := container.ChildNamed(key); existing != nil {
|
|
|
|
existing.Value = valueStr
|
|
|
|
existing.Style = style
|
|
|
|
existing.Tag = tag
|
|
|
|
existing.Kind = yaml.ScalarNode
|
|
|
|
existing.Path = path
|
2023-01-10 07:02:10 +00:00
|
|
|
existing.Type = kind
|
2022-12-20 05:49:37 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
newEntry := &Entry{
|
2022-11-22 06:16:54 +00:00
|
|
|
Path: path,
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Value: valueStr,
|
|
|
|
Style: style,
|
|
|
|
Tag: tag,
|
2023-01-10 07:02:10 +00:00
|
|
|
Type: kind,
|
2022-12-20 05:49:37 +00:00
|
|
|
}
|
|
|
|
if isNumeric(key) {
|
2023-01-11 04:50:06 +00:00
|
|
|
logrus.Debugf("hydrating sequence value at %s", path)
|
2022-12-20 05:49:37 +00:00
|
|
|
container.Kind = yaml.SequenceNode
|
|
|
|
container.Content = append(container.Content, newEntry)
|
|
|
|
} else {
|
2023-01-11 04:50:06 +00:00
|
|
|
logrus.Debugf("hydrating map value at %s", path)
|
2022-12-20 05:49:37 +00:00
|
|
|
keyEntry := NewEntry(key, yaml.ScalarNode)
|
|
|
|
keyEntry.Value = key
|
|
|
|
container.Content = append(container.Content, keyEntry, newEntry)
|
|
|
|
}
|
2022-11-22 06:16:54 +00:00
|
|
|
break
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
subContainer := container.ChildNamed(key)
|
|
|
|
if subContainer != nil {
|
2022-11-16 06:46:14 +00:00
|
|
|
container = subContainer
|
2023-01-11 04:50:06 +00:00
|
|
|
continue
|
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
|
2023-01-11 04:50:06 +00:00
|
|
|
kind := yaml.MappingNode
|
|
|
|
if idx+1 == len(path)-1 && isNumeric(path[idx+1]) {
|
|
|
|
logrus.Debugf("creating sequence container for key %s at %s", key, path)
|
|
|
|
kind = yaml.SequenceNode
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2023-01-11 04:50:06 +00:00
|
|
|
child := NewEntry(key, kind)
|
2023-01-15 07:17:34 +00:00
|
|
|
child.Path = append([]string{}, container.Path...)
|
|
|
|
child.Path = append(child.Path, key)
|
2023-01-11 04:50:06 +00:00
|
|
|
|
|
|
|
keyEntry := NewEntry(child.Name(), yaml.ScalarNode)
|
|
|
|
keyEntry.Value = key
|
|
|
|
container.Content = append(container.Content, keyEntry, child)
|
|
|
|
container = child
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) ToOP() []*op.ItemField {
|
2022-11-16 06:46:14 +00:00
|
|
|
ret := []*op.ItemField{}
|
|
|
|
var section *op.ItemSection
|
|
|
|
|
2023-01-11 04:50:06 +00:00
|
|
|
if e.IsScalar() {
|
2022-12-14 05:41:03 +00:00
|
|
|
name := e.Path[len(e.Path)-1]
|
2022-12-18 18:16:46 +00:00
|
|
|
fullPath := strings.Join(e.Path, ".")
|
2022-12-14 05:41:03 +00:00
|
|
|
if len(e.Path) > 1 {
|
2023-01-15 07:17:34 +00:00
|
|
|
section = &op.ItemSection{ID: e.Path[0], Label: e.Path[0]}
|
2022-12-14 05:41:03 +00:00
|
|
|
name = strings.Join(e.Path[1:], ".")
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
fieldType := "STRING"
|
|
|
|
if e.IsSecret() {
|
|
|
|
fieldType = "CONCEALED"
|
2022-12-18 18:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if annotationType := e.TypeStr(); annotationType != "" {
|
|
|
|
ret = append(ret, &op.ItemField{
|
|
|
|
ID: "~annotations." + fullPath,
|
|
|
|
Section: annotationsSection,
|
|
|
|
Label: fullPath,
|
|
|
|
Type: "STRING",
|
|
|
|
Value: annotationType,
|
|
|
|
})
|
2022-11-22 06:16:54 +00:00
|
|
|
}
|
2022-11-16 06:46:14 +00:00
|
|
|
|
|
|
|
ret = append(ret, &op.ItemField{
|
2022-12-18 18:16:46 +00:00
|
|
|
ID: fullPath,
|
2022-11-16 06:46:14 +00:00
|
|
|
Section: section,
|
|
|
|
Label: name,
|
2022-11-22 06:16:54 +00:00
|
|
|
Type: fieldType,
|
|
|
|
Value: e.Value,
|
2022-11-16 06:46:14 +00:00
|
|
|
})
|
2022-12-14 05:41:03 +00:00
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Kind == yaml.SequenceNode {
|
2022-11-22 06:16:54 +00:00
|
|
|
for _, child := range e.Content {
|
|
|
|
ret = append(ret, child.ToOP()...)
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
2022-12-14 05:41:03 +00:00
|
|
|
return ret
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-12-14 05:41:03 +00:00
|
|
|
for i := 0; i < len(e.Content); i += 2 {
|
|
|
|
child := e.Content[i+1]
|
|
|
|
if child.Type == YAMLTypeMetaConfig {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ret = append(ret, child.ToOP()...)
|
|
|
|
}
|
2022-11-16 06:46:14 +00:00
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
func (e *Entry) Name() string {
|
|
|
|
if e.Path == nil || len(e.Path) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return e.Path[len(e.Path)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Entry) AsMap() any {
|
2023-01-15 07:17:34 +00:00
|
|
|
if e.IsScalar() {
|
2022-12-23 05:38:37 +00:00
|
|
|
switch e.TypeStr() {
|
|
|
|
case "bool":
|
|
|
|
var boolVal bool
|
|
|
|
err := e.asNode().Decode(&boolVal)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not encode boolean at %s, %s", e.Path, err))
|
|
|
|
}
|
|
|
|
return boolVal
|
|
|
|
case "int":
|
|
|
|
var intVal uint64
|
|
|
|
err := e.asNode().Decode(&intVal)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not encode int at %s, %s", e.Path, err))
|
|
|
|
}
|
|
|
|
return intVal
|
|
|
|
case "float":
|
|
|
|
var floatVal float64
|
|
|
|
err := e.asNode().Decode(&floatVal)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("could not encode float at %s, %s", e.Path, err))
|
|
|
|
}
|
|
|
|
return floatVal
|
|
|
|
}
|
2022-12-20 05:49:37 +00:00
|
|
|
return e.String()
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
if e.Kind == yaml.SequenceNode {
|
|
|
|
ret := []any{}
|
2023-01-15 07:17:34 +00:00
|
|
|
for _, sub := range e.Content {
|
|
|
|
ret = append(ret, sub.AsMap())
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2022-11-22 06:16:54 +00:00
|
|
|
ret := map[string]any{}
|
2023-01-15 07:17:34 +00:00
|
|
|
for idx := 1; idx < len(e.Content); idx += 2 {
|
|
|
|
child := e.Content[idx]
|
2022-11-22 06:16:54 +00:00
|
|
|
ret[child.Name()] = child.AsMap()
|
2022-11-16 06:46:14 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
2022-12-18 18:16:46 +00:00
|
|
|
|
|
|
|
func (e *Entry) Merge(other *Entry) error {
|
|
|
|
if e.IsScalar() && other.IsScalar() {
|
|
|
|
e.Value = other.Value
|
|
|
|
e.Tag = other.Tag
|
|
|
|
e.Kind = other.Kind
|
|
|
|
e.Type = other.Type
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.Kind == yaml.MappingNode || e.Kind == yaml.DocumentNode {
|
|
|
|
for i := 0; i < len(other.Content); i += 2 {
|
|
|
|
remote := other.Content[i+1]
|
|
|
|
local := e.ChildNamed(remote.Name())
|
|
|
|
if local != nil {
|
|
|
|
if err := local.Merge(remote); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
e.Content = append(e.Content, NewEntry(remote.Name(), remote.Kind), remote)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, remote := range other.Content {
|
|
|
|
local := other.ChildNamed(remote.Name())
|
|
|
|
if local != nil {
|
|
|
|
if err := local.Merge(remote); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-01-11 04:50:06 +00:00
|
|
|
local.Content = append(local.Content, remote)
|
2022-12-18 18:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|