mirror of
https://github.com/getsops/sops.git
synced 2026-02-05 12:45:21 +01:00
Use yaml.v3 instead of modified yaml.v2 for handling YAML files (#791)
* Add another test (that currently fails). * First shot at using yaml.v3 for reading YAML files with comments. * Allow parsing multi-document YAML files. * Use Decoder to parse multi-part documents. * Use yaml.v3 for config and audit. * First step of serializing YAML using yaml.v3. * Always serialize with yaml.v3. * Remove debug prints. * Remove traces of github.com/mozilla-services/yaml. * Improve serialization of documents consisting only of comments. * Improve handling of some empty documents. * Adjust to latest changes in go-yaml/yaml#684. * Bump yaml.v3 version, temporarily disable failing tests. * Run go mod tidy. * Fix CI.
This commit is contained in:
@@ -12,7 +12,7 @@ import (
|
||||
// empty import as per https://godoc.org/github.com/lib/pq
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/mozilla-services/yaml"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mozilla.org/sops/v3/logging"
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
"github.com/mozilla-services/yaml"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.mozilla.org/sops/v3"
|
||||
"go.mozilla.org/sops/v3/age"
|
||||
|
||||
2
go.mod
2
go.mod
@@ -30,7 +30,6 @@ require (
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-wordwrap v1.0.0
|
||||
github.com/mozilla-services/yaml v0.0.0-20201007153854-c369669a6625
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v0.1.1 // indirect
|
||||
@@ -49,5 +48,6 @@ require (
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/ini.v1 v1.44.0
|
||||
gopkg.in/urfave/cli.v1 v1.20.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107172259-749611fa9fcc
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -201,8 +201,6 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mozilla-services/yaml v0.0.0-20201007153854-c369669a6625 h1:5IeGQzguDQ+EsTR5HE7tMYkZe09mqQ9cDypdKQEB5Kg=
|
||||
github.com/mozilla-services/yaml v0.0.0-20201007153854-c369669a6625/go.mod h1:Is/Ucts/yU/mWyGR8yELRoO46mejouKsJfQLAIfTR18=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -410,6 +408,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107172259-749611fa9fcc h1:XANm4xAMEQhRdWKqaL0qmhGDv7RuobwCO97TIlktaQE=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107172259-749611fa9fcc/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package yaml //import "go.mozilla.org/sops/v3/stores/yaml"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/mozilla-services/yaml"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.mozilla.org/sops/v3"
|
||||
"go.mozilla.org/sops/v3/stores"
|
||||
)
|
||||
@@ -12,102 +15,232 @@ import (
|
||||
type Store struct {
|
||||
}
|
||||
|
||||
func (store Store) mapSliceToTreeBranch(in yaml.MapSlice) sops.TreeBranch {
|
||||
branch := make(sops.TreeBranch, 0)
|
||||
for _, item := range in {
|
||||
if comment, ok := item.Key.(yaml.Comment); ok {
|
||||
// Convert the yaml comment to a generic sops comment
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: sops.Comment{
|
||||
Value: comment.Value,
|
||||
},
|
||||
Value: nil,
|
||||
})
|
||||
} else {
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: item.Key,
|
||||
Value: store.yamlValueToTreeValue(item.Value),
|
||||
})
|
||||
func (store Store) appendCommentToList(comment string, list []interface{}) []interface{} {
|
||||
if comment != "" {
|
||||
for _, commentLine := range strings.Split(comment, "\n") {
|
||||
if commentLine != "" {
|
||||
list = append(list, sops.Comment{
|
||||
Value: commentLine[1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (store Store) appendCommentToMap(comment string, branch sops.TreeBranch) sops.TreeBranch {
|
||||
if comment != "" {
|
||||
for _, commentLine := range strings.Split(comment, "\n") {
|
||||
if commentLine != "" {
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: sops.Comment{
|
||||
Value: commentLine[1:],
|
||||
},
|
||||
Value: nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return branch
|
||||
}
|
||||
|
||||
func (store Store) yamlValueToTreeValue(in interface{}) interface{} {
|
||||
switch in := in.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
return store.yamlMapToTreeBranch(in)
|
||||
case yaml.MapSlice:
|
||||
return store.mapSliceToTreeBranch(in)
|
||||
case []interface{}:
|
||||
return store.yamlSliceToTreeValue(in)
|
||||
case yaml.Comment:
|
||||
return sops.Comment{Value: in.Value}
|
||||
default:
|
||||
return in
|
||||
func (store Store) nodeToTreeValue(node *yaml.Node, commentsWereHandled bool) (interface{}, error) {
|
||||
switch node.Kind {
|
||||
case yaml.DocumentNode:
|
||||
panic("documents should never be passed here")
|
||||
case yaml.SequenceNode:
|
||||
var result []interface{}
|
||||
if !commentsWereHandled {
|
||||
result = store.appendCommentToList(node.HeadComment, result)
|
||||
result = store.appendCommentToList(node.LineComment, result)
|
||||
}
|
||||
for _, item := range node.Content {
|
||||
result = store.appendCommentToList(item.HeadComment, result)
|
||||
result = store.appendCommentToList(item.LineComment, result)
|
||||
val, err := store.nodeToTreeValue(item, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, val)
|
||||
result = store.appendCommentToList(item.FootComment, result)
|
||||
}
|
||||
if !commentsWereHandled {
|
||||
result = store.appendCommentToList(node.FootComment, result)
|
||||
}
|
||||
return result, nil
|
||||
case yaml.MappingNode:
|
||||
branch := make(sops.TreeBranch, 0)
|
||||
return store.appendYamlNodeToTreeBranch(node, branch, false)
|
||||
case yaml.ScalarNode:
|
||||
var result interface{}
|
||||
node.Decode(&result)
|
||||
return result, nil
|
||||
case yaml.AliasNode:
|
||||
return store.nodeToTreeValue(node.Alias, false);
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (store *Store) yamlSliceToTreeValue(in []interface{}) []interface{} {
|
||||
for i, v := range in {
|
||||
in[i] = store.yamlValueToTreeValue(v)
|
||||
func (store Store) appendYamlNodeToTreeBranch(node *yaml.Node, branch sops.TreeBranch, commentsWereHandled bool) (sops.TreeBranch, error) {
|
||||
var err error
|
||||
if !commentsWereHandled {
|
||||
branch = store.appendCommentToMap(node.HeadComment, branch)
|
||||
branch = store.appendCommentToMap(node.LineComment, branch)
|
||||
}
|
||||
return in
|
||||
switch node.Kind {
|
||||
case yaml.DocumentNode:
|
||||
for _, item := range node.Content {
|
||||
branch, err = store.appendYamlNodeToTreeBranch(item, branch, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case yaml.SequenceNode:
|
||||
return nil, fmt.Errorf("YAML documents that are sequences are not supported")
|
||||
case yaml.MappingNode:
|
||||
for i := 0; i < len(node.Content); i += 2 {
|
||||
key := node.Content[i]
|
||||
value := node.Content[i + 1]
|
||||
branch = store.appendCommentToMap(key.HeadComment, branch)
|
||||
branch = store.appendCommentToMap(key.LineComment, branch)
|
||||
handleValueComments := value.Kind == yaml.ScalarNode || value.Kind == yaml.AliasNode
|
||||
if handleValueComments {
|
||||
branch = store.appendCommentToMap(value.HeadComment, branch)
|
||||
branch = store.appendCommentToMap(value.LineComment, branch)
|
||||
}
|
||||
var keyValue interface{}
|
||||
key.Decode(&keyValue)
|
||||
valueTV, err := store.nodeToTreeValue(value, handleValueComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: keyValue,
|
||||
Value: valueTV,
|
||||
})
|
||||
if handleValueComments {
|
||||
branch = store.appendCommentToMap(value.FootComment, branch)
|
||||
}
|
||||
branch = store.appendCommentToMap(key.FootComment, branch)
|
||||
}
|
||||
case yaml.ScalarNode:
|
||||
// A empty document with a document start marker without comments results in null
|
||||
if node.ShortTag() == "!!null" {
|
||||
return branch, nil
|
||||
}
|
||||
return nil, fmt.Errorf("YAML documents that are values are not supported")
|
||||
case yaml.AliasNode:
|
||||
branch, err = store.appendYamlNodeToTreeBranch(node.Alias, branch, false)
|
||||
}
|
||||
if !commentsWereHandled {
|
||||
branch = store.appendCommentToMap(node.FootComment, branch)
|
||||
}
|
||||
return branch, nil
|
||||
}
|
||||
|
||||
func (store *Store) yamlMapToTreeBranch(in map[interface{}]interface{}) sops.TreeBranch {
|
||||
func (store Store) yamlDocumentNodeToTreeBranch(in yaml.Node) (sops.TreeBranch, error) {
|
||||
branch := make(sops.TreeBranch, 0)
|
||||
for k, v := range in {
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: k.(string),
|
||||
Value: store.yamlValueToTreeValue(v),
|
||||
})
|
||||
}
|
||||
return branch
|
||||
return store.appendYamlNodeToTreeBranch(&in, branch, false)
|
||||
}
|
||||
|
||||
func (store Store) treeValueToYamlValue(in interface{}) interface{} {
|
||||
func (store *Store) addCommentsHead(node *yaml.Node, comments []string) []string {
|
||||
if len(comments) > 0 {
|
||||
comment := "#" + strings.Join(comments, "\n#")
|
||||
if len(node.HeadComment) > 0 {
|
||||
node.HeadComment = comment + "\n" + node.HeadComment
|
||||
} else {
|
||||
node.HeadComment = comment
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) addCommentsFoot(node *yaml.Node, comments []string) []string {
|
||||
if len(comments) > 0 {
|
||||
comment := "#" + strings.Join(comments, "\n#")
|
||||
if len(node.FootComment) > 0 {
|
||||
node.FootComment += "\n" + comment
|
||||
} else {
|
||||
node.FootComment = comment
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) treeValueToNode(in interface{}) *yaml.Node {
|
||||
switch in := in.(type) {
|
||||
case sops.TreeBranch:
|
||||
return store.treeBranchToYamlMap(in)
|
||||
case sops.Comment:
|
||||
return yaml.Comment{in.Value}
|
||||
var mapping = &yaml.Node{}
|
||||
mapping.Kind = yaml.MappingNode
|
||||
store.appendTreeBranch(in, mapping)
|
||||
return mapping
|
||||
case []interface{}:
|
||||
var out []interface{}
|
||||
for _, v := range in {
|
||||
out = append(out, store.treeValueToYamlValue(v))
|
||||
}
|
||||
return out
|
||||
var sequence = &yaml.Node{}
|
||||
sequence.Kind = yaml.SequenceNode
|
||||
store.appendSequence(in, sequence)
|
||||
return sequence
|
||||
default:
|
||||
return in
|
||||
var valueNode = &yaml.Node{}
|
||||
valueNode.Encode(in)
|
||||
return valueNode
|
||||
}
|
||||
}
|
||||
|
||||
func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice {
|
||||
branch := make(yaml.MapSlice, 0)
|
||||
func (store *Store) appendSequence(in []interface{}, sequence *yaml.Node) {
|
||||
var comments []string
|
||||
var beginning bool = true
|
||||
for _, item := range in {
|
||||
if comment, ok := item.Key.(sops.Comment); ok {
|
||||
branch = append(branch, yaml.MapItem{
|
||||
Key: store.treeValueToYamlValue(comment),
|
||||
Value: nil,
|
||||
})
|
||||
if comment, ok := item.(sops.Comment); ok {
|
||||
comments = append(comments, comment.Value)
|
||||
} else {
|
||||
branch = append(branch, yaml.MapItem{
|
||||
Key: item.Key,
|
||||
Value: store.treeValueToYamlValue(item.Value),
|
||||
})
|
||||
if beginning {
|
||||
comments = store.addCommentsHead(sequence, comments)
|
||||
beginning = false
|
||||
}
|
||||
itemNode := store.treeValueToNode(item)
|
||||
comments = store.addCommentsHead(itemNode, comments)
|
||||
sequence.Content = append(sequence.Content, itemNode)
|
||||
}
|
||||
}
|
||||
if len(comments) > 0 {
|
||||
if beginning {
|
||||
comments = store.addCommentsHead(sequence, comments)
|
||||
} else {
|
||||
comments = store.addCommentsFoot(sequence.Content[len(sequence.Content) - 1], comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) appendTreeBranch(branch sops.TreeBranch, mapping *yaml.Node) {
|
||||
var comments []string
|
||||
var beginning bool = true
|
||||
for _, item := range branch {
|
||||
if comment, ok := item.Key.(sops.Comment); ok {
|
||||
comments = append(comments, comment.Value)
|
||||
} else {
|
||||
if beginning {
|
||||
comments = store.addCommentsHead(mapping, comments)
|
||||
beginning = false
|
||||
}
|
||||
var keyNode = &yaml.Node{}
|
||||
keyNode.Encode(item.Key)
|
||||
comments = store.addCommentsHead(keyNode, comments)
|
||||
valueNode := store.treeValueToNode(item.Value)
|
||||
mapping.Content = append(mapping.Content, keyNode, valueNode)
|
||||
}
|
||||
}
|
||||
if len(comments) > 0 {
|
||||
if beginning {
|
||||
comments = store.addCommentsHead(mapping, comments)
|
||||
} else {
|
||||
comments = store.addCommentsFoot(mapping.Content[len(mapping.Content) - 1], comments)
|
||||
}
|
||||
}
|
||||
return branch
|
||||
}
|
||||
|
||||
// LoadEncryptedFile loads the contents of an encrypted yaml file onto a
|
||||
// sops.Tree runtime object
|
||||
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
||||
var data []yaml.MapSlice
|
||||
if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil {
|
||||
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
// Because we don't know what fields the input file will have, we have to
|
||||
// load the file in two steps.
|
||||
// First, we load the file's metadata, the structure of which is known.
|
||||
@@ -123,14 +256,33 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
||||
if err != nil {
|
||||
return sops.Tree{}, err
|
||||
}
|
||||
var data yaml.Node
|
||||
if err := yaml.Unmarshal(in, &data); err != nil {
|
||||
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
var branches sops.TreeBranches
|
||||
for _, doc := range data {
|
||||
for i, item := range doc {
|
||||
if item.Key == "sops" { // Erase
|
||||
doc = append(doc[:i], doc[i+1:]...)
|
||||
d := yaml.NewDecoder(bytes.NewReader(in))
|
||||
for true {
|
||||
var data yaml.Node
|
||||
err := d.Decode(&data)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
|
||||
branch, err := store.yamlDocumentNodeToTreeBranch(data)
|
||||
if err != nil {
|
||||
return sops.Tree{}, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
|
||||
for i, elt := range branch {
|
||||
if elt.Key == "sops" { // Erase
|
||||
branch = append(branch[:i], branch[i+1:]...)
|
||||
}
|
||||
}
|
||||
branches = append(branches, store.mapSliceToTreeBranch(doc))
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
return sops.Tree{
|
||||
Branches: branches,
|
||||
@@ -139,16 +291,25 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
||||
}
|
||||
|
||||
// LoadPlainFile loads the contents of a plaintext yaml file onto a
|
||||
// sops.Tree runtime obejct
|
||||
// sops.Tree runtime object
|
||||
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
||||
var data []yaml.MapSlice
|
||||
if err := (yaml.CommentUnmarshaler{}).UnmarshalDocuments(in, &data); err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
|
||||
var branches sops.TreeBranches
|
||||
for _, doc := range data {
|
||||
branches = append(branches, store.mapSliceToTreeBranch(doc))
|
||||
d := yaml.NewDecoder(bytes.NewReader(in))
|
||||
for true {
|
||||
var data yaml.Node
|
||||
err := d.Decode(&data)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
|
||||
branch, err := store.yamlDocumentNodeToTreeBranch(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshaling input YAML: %s", err)
|
||||
}
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
return branches, nil
|
||||
}
|
||||
@@ -156,45 +317,70 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
||||
// EmitEncryptedFile returns the encrypted bytes of the yaml file corresponding to a
|
||||
// sops.Tree runtime object
|
||||
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
|
||||
out := []byte{}
|
||||
for i, branch := range in.Branches {
|
||||
if i > 0 {
|
||||
out = append(out, "---\n"...)
|
||||
}
|
||||
yamlMap := store.treeBranchToYamlMap(branch)
|
||||
yamlMap = append(yamlMap, yaml.MapItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)})
|
||||
tout, err := (&yaml.YAMLMarshaler{Indent: 4}).Marshal(yamlMap)
|
||||
var b bytes.Buffer
|
||||
e := yaml.NewEncoder(io.Writer(&b))
|
||||
e.SetIndent(4)
|
||||
for _, branch := range in.Branches {
|
||||
// Document root
|
||||
var doc = yaml.Node{}
|
||||
doc.Kind = yaml.DocumentNode
|
||||
// Add global mapping
|
||||
var mapping = yaml.Node{}
|
||||
mapping.Kind = yaml.MappingNode
|
||||
doc.Content = append(doc.Content, &mapping)
|
||||
// Create copy of branch with metadata appended
|
||||
branch = append(sops.TreeBranch(nil), branch...)
|
||||
branch = append(branch, sops.TreeItem{
|
||||
Key: "sops",
|
||||
Value: stores.MetadataFromInternal(in.Metadata),
|
||||
})
|
||||
// Marshal branch to global mapping node
|
||||
store.appendTreeBranch(branch, &mapping)
|
||||
// Encode YAML
|
||||
err := e.Encode(&doc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
|
||||
}
|
||||
out = append(out, tout...)
|
||||
}
|
||||
return out, nil
|
||||
e.Close()
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// EmitPlainFile returns the plaintext bytes of the yaml file corresponding to a
|
||||
// sops.TreeBranches runtime object
|
||||
func (store *Store) EmitPlainFile(branches sops.TreeBranches) ([]byte, error) {
|
||||
var out []byte
|
||||
for i, branch := range branches {
|
||||
if i > 0 {
|
||||
out = append(out, "---\n"...)
|
||||
var b bytes.Buffer
|
||||
e := yaml.NewEncoder(io.Writer(&b))
|
||||
e.SetIndent(4)
|
||||
for _, branch := range branches {
|
||||
// Document root
|
||||
var doc = yaml.Node{}
|
||||
doc.Kind = yaml.DocumentNode
|
||||
// Add global mapping
|
||||
var mapping = yaml.Node{}
|
||||
mapping.Kind = yaml.MappingNode
|
||||
// Marshal branch to global mapping node
|
||||
store.appendTreeBranch(branch, &mapping)
|
||||
if len(mapping.Content) == 0 {
|
||||
doc.HeadComment = mapping.HeadComment
|
||||
} else {
|
||||
doc.Content = append(doc.Content, &mapping)
|
||||
}
|
||||
yamlMap := store.treeBranchToYamlMap(branch)
|
||||
tmpout, err := (&yaml.YAMLMarshaler{Indent: 4}).Marshal(yamlMap)
|
||||
// Encode YAML
|
||||
err := e.Encode(&doc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshaling to yaml: %s", err)
|
||||
}
|
||||
out = append(out[:], tmpout[:]...)
|
||||
}
|
||||
return out, nil
|
||||
e.Close()
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// EmitValue returns bytes corresponding to a single encoded value
|
||||
// in a generic interface{} object
|
||||
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
|
||||
v = store.treeValueToYamlValue(v)
|
||||
return (&yaml.YAMLMarshaler{Indent: 4}).Marshal(v)
|
||||
n := store.treeValueToNode(v)
|
||||
return yaml.Marshal(n)
|
||||
}
|
||||
|
||||
// EmitExample returns the bytes corresponding to an example complex tree
|
||||
|
||||
@@ -15,6 +15,12 @@ key1_a: value
|
||||
---
|
||||
key2: value2`)
|
||||
|
||||
var PLAIN_0 = []byte(`# comment 0
|
||||
key1: value
|
||||
key1_a: value
|
||||
# ^ comment 1
|
||||
`)
|
||||
|
||||
var BRANCHES = sops.TreeBranches{
|
||||
sops.TreeBranch{
|
||||
sops.TreeItem{
|
||||
@@ -73,8 +79,16 @@ prometheus-node-exporter:
|
||||
##
|
||||
jobLabel: node-exporter
|
||||
extraArgs:
|
||||
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
|
||||
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
|
||||
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/docker/.+)($|/)
|
||||
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
|
||||
`)
|
||||
|
||||
var COMMENT_4 = []byte(`# foo
|
||||
`)
|
||||
|
||||
var COMMENT_5 = []byte(`# foo
|
||||
---
|
||||
key: value
|
||||
`)
|
||||
|
||||
func TestUnmarshalMetadataFromNonSOPSFile(t *testing.T) {
|
||||
@@ -95,6 +109,7 @@ func TestComment1(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(COMMENT_1), string(bytes))
|
||||
assert.Equal(t, COMMENT_1, bytes)
|
||||
}
|
||||
|
||||
@@ -104,6 +119,7 @@ func TestComment2(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(COMMENT_2), string(bytes))
|
||||
assert.Equal(t, COMMENT_2, bytes)
|
||||
}
|
||||
|
||||
@@ -113,5 +129,59 @@ func TestComment3(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(COMMENT_3_OUT), string(bytes))
|
||||
assert.Equal(t, COMMENT_3_OUT, bytes)
|
||||
}
|
||||
|
||||
/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
|
||||
func TestComment4(t *testing.T) {
|
||||
// First iteration: load and store
|
||||
branches, err := (&Store{}).LoadPlainFile(COMMENT_4)
|
||||
assert.Nil(t, err)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(COMMENT_4), string(bytes))
|
||||
assert.Equal(t, COMMENT_4, bytes)
|
||||
}
|
||||
|
||||
func TestComment5(t *testing.T) {
|
||||
// First iteration: load and store
|
||||
branches, err := (&Store{}).LoadPlainFile(COMMENT_5)
|
||||
assert.Nil(t, err)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(COMMENT_5), string(bytes))
|
||||
assert.Equal(t, COMMENT_5, bytes)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
// First iteration: load and store
|
||||
branches, err := (&Store{}).LoadPlainFile([]byte(``))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(branches), 0)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ``, string(bytes))
|
||||
}
|
||||
|
||||
/* TODO: re-enable once https://github.com/go-yaml/yaml/pull/690 is merged
|
||||
func TestEmpty2(t *testing.T) {
|
||||
// First iteration: load and store
|
||||
branches, err := (&Store{}).LoadPlainFile([]byte(`---`))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(branches), 1)
|
||||
assert.Equal(t, len(branches[0]), 0)
|
||||
bytes, err := (&Store{}).EmitPlainFile(branches)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ``, string(bytes))
|
||||
}
|
||||
*/
|
||||
|
||||
func TestEmitValue(t *testing.T) {
|
||||
// First iteration: load and store
|
||||
bytes, err := (&Store{}).EmitValue(BRANCHES[0])
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(PLAIN_0), string(bytes))
|
||||
assert.Equal(t, PLAIN_0, bytes)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user