Skip to content

Commit ddd46bb

Browse files
committed
Add common code to encode and decode metadata.
Signed-off-by: Felix Fontein <felix@fontein.de>
1 parent aa36401 commit ddd46bb

8 files changed

Lines changed: 1743 additions & 227 deletions

File tree

stores/dotenv/store.go

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package dotenv //import "github.com/getsops/sops/v3/stores/dotenv"
33
import (
44
"bytes"
55
"fmt"
6-
"sort"
76
"strings"
87

98
"github.com/getsops/sops/v3"
@@ -33,43 +32,15 @@ func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
3332
if err != nil {
3433
return sops.Tree{}, err
3534
}
36-
37-
var resultBranch sops.TreeBranch
38-
mdMap := make(map[string]interface{})
39-
for _, item := range branches[0] {
40-
switch key := item.Key.(type) {
41-
case string:
42-
if strings.HasPrefix(key, SopsPrefix) {
43-
key = key[len(SopsPrefix):]
44-
mdMap[key] = item.Value
45-
} else {
46-
resultBranch = append(resultBranch, item)
47-
}
48-
case sops.Comment:
49-
resultBranch = append(resultBranch, item)
50-
default:
51-
panic(fmt.Sprintf("Unexpected type: %T (value %#v)", key, key))
52-
}
53-
}
54-
if len(mdMap) == 0 {
55-
return sops.Tree{}, sops.MetadataNotFound
56-
}
57-
58-
stores.DecodeNewLines(mdMap)
59-
metadata, err := stores.UnflattenMetadata(mdMap)
35+
branches, metadata, err := stores.ExtractMetadata(branches, stores.MetadataOpts{
36+
Flatten: stores.MetadataFlattenFull,
37+
})
6038
if err != nil {
6139
return sops.Tree{}, err
6240
}
63-
internalMetadata, err := metadata.ToInternal()
64-
if err != nil {
65-
return sops.Tree{}, err
66-
}
67-
6841
return sops.Tree{
69-
Branches: sops.TreeBranches{
70-
resultBranch,
71-
},
72-
Metadata: internalMetadata,
42+
Branches: branches,
43+
Metadata: metadata,
7344
}, nil
7445
}
7546

@@ -107,29 +78,13 @@ func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
10778
// EmitEncryptedFile returns the encrypted file's bytes corresponding to a sops
10879
// runtime object
10980
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
110-
metadata := stores.MetadataFromInternal(in.Metadata)
111-
mdItems, err := stores.FlattenMetadata(metadata)
81+
branches, err := stores.SerializeMetadata(in, stores.MetadataOpts{
82+
Flatten: stores.MetadataFlattenFull,
83+
})
11284
if err != nil {
113-
return nil, err
114-
}
115-
116-
stores.EncodeNonStrings(mdItems)
117-
stores.EncodeNewLines(mdItems)
118-
119-
var keys []string
120-
for k := range mdItems {
121-
keys = append(keys, k)
122-
}
123-
sort.Strings(keys)
124-
125-
for _, key := range keys {
126-
var value = mdItems[key]
127-
if value == nil {
128-
continue
129-
}
130-
in.Branches[0] = append(in.Branches[0], sops.TreeItem{Key: SopsPrefix + key, Value: value})
85+
return nil, fmt.Errorf("Error marshaling metadata: %s", err)
13186
}
132-
return store.EmitPlainFile(in.Branches)
87+
return store.EmitPlainFile(branches)
13388
}
13489

13590
// EmitPlainFile returns the plaintext file's bytes corresponding to a sops

stores/flatten.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package stores
22

33
import (
44
"fmt"
5+
"reflect"
6+
"slices"
57
"strconv"
68
"strings"
79

810
"github.com/go-viper/mapstructure/v2"
11+
12+
"github.com/getsops/sops/v3"
913
)
1014

1115
const mapSeparator = "__map_"
@@ -270,3 +274,154 @@ func EncodeNonStrings(m map[string]interface{}) {
270274
}
271275
}
272276
}
277+
278+
func descendMap(branch sops.TreeBranch, key string) (sops.TreeBranch, reflect.Value) {
279+
for idx, elt := range branch {
280+
if elt.Key == key {
281+
return branch, reflect.ValueOf(&branch[idx].Value)
282+
}
283+
}
284+
newBranch := append(branch, sops.TreeItem{
285+
Key: key,
286+
Value: nil,
287+
})
288+
return newBranch, reflect.ValueOf(&newBranch[len(newBranch)-1].Value)
289+
}
290+
291+
func descendList(array []interface{}, index int) ([]interface{}, reflect.Value) {
292+
if index >= len(array) {
293+
if index >= cap(array) {
294+
array = slices.Grow(array, index+1-cap(array))
295+
}
296+
array = array[:index+1]
297+
}
298+
return array, reflect.ValueOf(&array[index])
299+
}
300+
301+
func descend(value reflect.Value, t token) (reflect.Value, error) {
302+
interfaceType := reflect.TypeOf((*interface{})(nil))
303+
switch currToken := t.(type) {
304+
case mapToken:
305+
// This special case is only needed for the root
306+
treeBranchPtrType := reflect.TypeOf((*sops.TreeBranch)(nil))
307+
if value.Type() == treeBranchPtrType {
308+
v := *value.Interface().(*sops.TreeBranch)
309+
v, nextVal := descendMap(v, currToken.key)
310+
reflect.Indirect(value).Set(reflect.ValueOf(v))
311+
return nextVal, nil
312+
}
313+
if value.Type() == interfaceType {
314+
val := reflect.Indirect(value)
315+
if val.IsNil() {
316+
v, nextVal := descendMap(nil, currToken.key)
317+
val.Set(reflect.ValueOf(v))
318+
return nextVal, nil
319+
}
320+
if v, ok := val.Interface().(sops.TreeBranch); ok {
321+
v, nextVal := descendMap(v, currToken.key)
322+
val.Set(reflect.ValueOf(v))
323+
return nextVal, nil
324+
}
325+
}
326+
return reflect.Value{}, fmt.Errorf("Type mismatch: can only use string key for map")
327+
case listToken:
328+
if value.Type() == interfaceType {
329+
val := reflect.Indirect(value)
330+
if val.IsNil() {
331+
v, nextVal := descendList(nil, currToken.position)
332+
val.Set(reflect.ValueOf(v))
333+
return nextVal, nil
334+
}
335+
if v, ok := val.Interface().([]interface{}); ok {
336+
v, nextVal := descendList(v, currToken.position)
337+
val.Set(reflect.ValueOf(v))
338+
return nextVal, nil
339+
}
340+
}
341+
return reflect.Value{}, fmt.Errorf("Type mismatch: can only use integer key for list")
342+
default:
343+
return reflect.Value{}, fmt.Errorf("Internal error: unknown token %q", t)
344+
}
345+
}
346+
347+
func unflattenTreeBranch(branch sops.TreeBranch) (sops.TreeBranch, error) {
348+
var result sops.TreeBranch
349+
for _, item := range branch {
350+
if _, ok := item.Key.(sops.Comment); ok {
351+
continue
352+
}
353+
if key, ok := item.Key.(string); ok {
354+
current := reflect.ValueOf(&result)
355+
tokens := tokenize(key)
356+
for _, token := range tokens {
357+
var err error
358+
current, err = descend(current, token)
359+
if err != nil {
360+
return nil, fmt.Errorf("Error while unflattening %q: %w", key, err)
361+
}
362+
}
363+
reflect.Indirect(current).Set(reflect.ValueOf(item.Value))
364+
continue
365+
} else {
366+
return nil, fmt.Errorf("Found non-string key %q when unflattening", item.Key)
367+
}
368+
}
369+
return result, nil
370+
}
371+
372+
func flattenDescendValue(value interface{}, key string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
373+
switch value := value.(type) {
374+
case sops.TreeBranch:
375+
return flattenDescendMap(value, key+mapSeparator, destination, destinationMap)
376+
case []interface{}:
377+
return flattenDescendArray(value, key+listSeparator, destination, destinationMap)
378+
}
379+
if _, ok := (*destinationMap)[key]; ok {
380+
return nil, fmt.Errorf("Found key collision %q while flattening", key)
381+
}
382+
destination = append(destination, sops.TreeItem{
383+
Key: key,
384+
Value: value,
385+
})
386+
(*destinationMap)[key] = true
387+
return destination, nil
388+
}
389+
390+
func flattenDescendMap(branch sops.TreeBranch, prefix string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
391+
for _, item := range branch {
392+
if _, ok := item.Key.(sops.Comment); ok {
393+
continue
394+
}
395+
if key, ok := item.Key.(string); ok {
396+
var err error
397+
destination, err = flattenDescendValue(item.Value, prefix+key, destination, destinationMap)
398+
if err != nil {
399+
return nil, err
400+
}
401+
} else {
402+
return nil, fmt.Errorf("Found non-string key %q when flattening", item.Key)
403+
}
404+
}
405+
return destination, nil
406+
}
407+
408+
func flattenDescendArray(array []interface{}, prefix string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
409+
i := 0
410+
for _, item := range array {
411+
if _, ok := item.(sops.Comment); ok {
412+
continue
413+
}
414+
var err error
415+
destination, err = flattenDescendValue(item, fmt.Sprintf("%s%d", prefix, i), destination, destinationMap)
416+
if err != nil {
417+
return nil, err
418+
}
419+
i += 1
420+
}
421+
return destination, nil
422+
}
423+
424+
func flattenTreeBranch(branch sops.TreeBranch, prefix string) (sops.TreeBranch, error) {
425+
destinationMap := map[string]bool{}
426+
return flattenDescendMap(branch, prefix, nil, &destinationMap)
427+
}

0 commit comments

Comments
 (0)