288 lines
6.2 KiB
Go
288 lines
6.2 KiB
Go
// Copyright © 2022 Roberto Hidalgo <joao@un.rob.mx>
|
|
//
|
|
// 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 config
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
op "github.com/1Password/connect-sdk-go/onepassword"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func isNumeric(s string) bool {
|
|
for _, v := range s {
|
|
if v < '0' || v > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
type Entry struct {
|
|
Key string
|
|
Path []string
|
|
Value interface{}
|
|
Children map[string]*Entry
|
|
isSecret bool
|
|
isSequence bool
|
|
node *yaml.Node
|
|
}
|
|
|
|
func NewEntry(name string) *Entry {
|
|
return &Entry{Key: name, Children: map[string]*Entry{}}
|
|
}
|
|
|
|
func (e *Entry) SetKey(key string, parent []string) {
|
|
e.Path = append(parent, key)
|
|
e.Key = key
|
|
for k, child := range e.Children {
|
|
child.SetKey(k, e.Path)
|
|
}
|
|
}
|
|
|
|
func (e *Entry) UnmarshalYAML(node *yaml.Node) error {
|
|
e.node = node
|
|
switch node.Kind {
|
|
case yaml.DocumentNode, yaml.MappingNode:
|
|
if e.Children == nil {
|
|
e.Children = map[string]*Entry{}
|
|
}
|
|
err := node.Decode(&e.Children)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
case yaml.SequenceNode:
|
|
list := []*Entry{}
|
|
err := node.Decode(&list)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if e.Children == nil {
|
|
e.Children = map[string]*Entry{}
|
|
}
|
|
for idx, child := range list {
|
|
child.Key = fmt.Sprintf("%d", idx)
|
|
e.Children[child.Key] = child
|
|
}
|
|
e.isSequence = true
|
|
case yaml.ScalarNode:
|
|
var val interface{}
|
|
err := node.Decode(&val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.Value = val
|
|
e.isSecret = node.Tag == "!!secret"
|
|
default:
|
|
return fmt.Errorf("unknown yaml type: %v", node.Kind)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *Entry) MarshalYAML() (interface{}, error) {
|
|
if len(e.Children) == 0 {
|
|
if redactOutput && e.isSecret {
|
|
n := e.node
|
|
return &yaml.Node{
|
|
Kind: n.Kind,
|
|
Style: yaml.TaggedStyle,
|
|
Tag: n.Tag,
|
|
Value: "",
|
|
HeadComment: n.HeadComment,
|
|
LineComment: n.LineComment,
|
|
FootComment: n.FootComment,
|
|
Line: n.Line,
|
|
Column: n.Column,
|
|
}, nil
|
|
}
|
|
return e.node, nil
|
|
}
|
|
|
|
if e.isSequence {
|
|
ret := make([]*Entry, len(e.Children))
|
|
for k, child := range e.Children {
|
|
idx, _ := strconv.Atoi(k)
|
|
ret[idx] = child
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
ret := map[string]*Entry{}
|
|
for k, child := range e.Children {
|
|
ret[k] = child
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (e *Entry) FromOP(fields []*op.ItemField) error {
|
|
annotations := map[string]string{}
|
|
data := map[string]string{}
|
|
labels := []string{}
|
|
|
|
for i := 0; i < len(fields); i++ {
|
|
field := fields[i]
|
|
label := field.Label
|
|
if field.Section != nil {
|
|
if field.Section.Label == "~annotations" {
|
|
annotations[label] = field.Value
|
|
continue
|
|
} else {
|
|
label = field.Section.Label + "." + label
|
|
}
|
|
}
|
|
labels = append(labels, label)
|
|
data[label] = field.Value
|
|
}
|
|
|
|
for _, label := range labels {
|
|
var value interface{} = data[label]
|
|
|
|
if typeString, ok := annotations[label]; ok {
|
|
switch typeString {
|
|
case "bool":
|
|
value = value == "true"
|
|
case "int":
|
|
var err error
|
|
value, err = strconv.ParseInt(value.(string), 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
path := strings.Split(label, ".")
|
|
container := e
|
|
|
|
for idx, key := range path {
|
|
if idx == len(path)-1 {
|
|
if !isNumeric(key) {
|
|
isSecretLabel := annotations[label]
|
|
container.Children[key] = &Entry{
|
|
Key: key,
|
|
Path: path,
|
|
Value: value,
|
|
isSecret: isSecretLabel == "!!secret",
|
|
}
|
|
break
|
|
}
|
|
|
|
holderI := container.Value
|
|
if container.Value == nil {
|
|
holderI = []interface{}{}
|
|
}
|
|
|
|
holder := holderI.([]interface{})
|
|
container.Value = append(holder, value)
|
|
break
|
|
}
|
|
|
|
subContainer, exists := container.Children[key]
|
|
if exists {
|
|
container = subContainer
|
|
} else {
|
|
container.Children[key] = NewEntry(key)
|
|
container = container.Children[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *Entry) ToOP(annotationsSection *op.ItemSection) []*op.ItemField {
|
|
ret := []*op.ItemField{}
|
|
var section *op.ItemSection
|
|
name := e.Key
|
|
if len(e.Path) > 1 {
|
|
section = &op.ItemSection{ID: e.Path[0]}
|
|
name = strings.Join(e.Path[1:], ".")
|
|
}
|
|
|
|
if e.isSecret {
|
|
ret = append(ret, &op.ItemField{
|
|
ID: "~annotations." + strings.Join(e.Path, "."),
|
|
Section: annotationsSection,
|
|
Label: name,
|
|
Type: "STRING",
|
|
Value: "secret",
|
|
})
|
|
} else if _, ok := e.Value.(bool); ok {
|
|
ret = append(ret, &op.ItemField{
|
|
ID: "~annotations." + strings.Join(e.Path, "."),
|
|
Section: annotationsSection,
|
|
Label: name,
|
|
Type: "STRING",
|
|
Value: "bool",
|
|
})
|
|
} else if _, ok := e.Value.(int); ok {
|
|
ret = append(ret, &op.ItemField{
|
|
ID: "~annotations." + strings.Join(e.Path, "."),
|
|
Section: annotationsSection,
|
|
Label: name,
|
|
Type: "STRING",
|
|
Value: "int",
|
|
})
|
|
} else if _, ok := e.Value.(float64); ok {
|
|
ret = append(ret, &op.ItemField{
|
|
ID: "~annotations." + strings.Join(e.Path, "."),
|
|
Section: annotationsSection,
|
|
Label: name,
|
|
Type: "STRING",
|
|
Value: "float",
|
|
})
|
|
}
|
|
|
|
if len(e.Children) == 0 {
|
|
ret = append(ret, &op.ItemField{
|
|
ID: strings.Join(e.Path, "."),
|
|
Section: section,
|
|
Label: name,
|
|
Type: "STRING",
|
|
Value: fmt.Sprintf("%s", e.Value),
|
|
})
|
|
} else {
|
|
for _, child := range e.Children {
|
|
ret = append(ret, child.ToOP(annotationsSection)...)
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (e *Entry) AsMap() interface{} {
|
|
if len(e.Children) == 0 {
|
|
if redactOutput && e.isSecret {
|
|
return ""
|
|
}
|
|
return e.Value
|
|
}
|
|
|
|
if e.isSequence {
|
|
ret := make([]interface{}, len(e.Children))
|
|
for key, child := range e.Children {
|
|
idx, _ := strconv.Atoi(key)
|
|
ret[idx] = child.AsMap()
|
|
}
|
|
return ret
|
|
}
|
|
|
|
ret := map[string]interface{}{}
|
|
for key, child := range e.Children {
|
|
ret[key] = child.AsMap()
|
|
}
|
|
return ret
|
|
}
|