2017-08-23 16:36:49 -07:00
|
|
|
package json //import "go.mozilla.org/sops/stores/json"
|
2016-08-12 16:51:01 -07:00
|
|
|
|
|
|
|
|
import (
|
2016-09-01 14:16:41 -07:00
|
|
|
"bytes"
|
2016-08-12 16:51:01 -07:00
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2016-09-01 14:16:41 -07:00
|
|
|
"io"
|
2017-08-23 16:36:49 -07:00
|
|
|
|
2016-10-31 17:19:26 -04:00
|
|
|
"go.mozilla.org/sops"
|
2017-08-23 16:36:49 -07:00
|
|
|
"go.mozilla.org/sops/stores"
|
2016-08-12 16:51:01 -07:00
|
|
|
)
|
|
|
|
|
|
2016-09-06 16:06:27 -07:00
|
|
|
// Store handles storage of JSON data.
|
2016-08-24 10:29:28 -07:00
|
|
|
type Store struct {
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
2016-09-06 16:06:27 -07:00
|
|
|
// BinaryStore handles storage of binary data in a JSON envelope.
|
|
|
|
|
type BinaryStore struct {
|
|
|
|
|
store Store
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// LoadEncryptedFile loads an encrypted json file onto a sops.Tree object
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store BinaryStore) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
|
|
|
|
return store.store.LoadEncryptedFile(in)
|
2016-09-06 16:06:27 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// LoadPlainFile loads a plaintext json file onto a sops.Tree encapsulated
|
|
|
|
|
// within a sops.TreeBranches object
|
2018-09-20 11:18:34 -06:00
|
|
|
func (store BinaryStore) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
|
|
|
|
return sops.TreeBranches{
|
|
|
|
|
sops.TreeBranch{
|
|
|
|
|
sops.TreeItem{
|
|
|
|
|
Key: "data",
|
|
|
|
|
Value: string(in),
|
|
|
|
|
},
|
2018-04-20 10:13:46 +02:00
|
|
|
},
|
|
|
|
|
}, nil
|
2016-09-06 16:06:27 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitEncryptedFile produces an encrypted json file's bytes from its corresponding sops.Tree object
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store BinaryStore) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
|
|
|
|
|
return store.store.EmitEncryptedFile(in)
|
2016-11-18 14:52:13 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitPlainFile produces plaintext json file's bytes from its corresponding sops.TreeBranches object
|
2018-09-20 11:18:34 -06:00
|
|
|
func (store BinaryStore) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
|
|
|
|
|
// JSON stores a single object per file
|
|
|
|
|
for _, item := range in[0] {
|
2018-04-20 10:13:46 +02:00
|
|
|
if item.Key == "data" {
|
|
|
|
|
return []byte(item.Value.(string)), nil
|
|
|
|
|
}
|
2016-09-06 16:06:27 -07:00
|
|
|
}
|
2018-04-20 10:13:46 +02:00
|
|
|
return nil, fmt.Errorf("No binary data found in tree")
|
2016-09-06 16:06:27 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitValue extracts a value from a generic interface{} object representing a structured set
|
|
|
|
|
// of binary files
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store BinaryStore) EmitValue(v interface{}) ([]byte, error) {
|
|
|
|
|
return nil, fmt.Errorf("Binary files are not structured and extracting a single value is not possible")
|
2016-09-06 16:06:27 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitExample returns the example's plaintext json file bytes
|
2019-01-23 10:51:47 +01:00
|
|
|
func (store BinaryStore) EmitExample() []byte {
|
|
|
|
|
return []byte("Welcome to SOPS! Edit this file as you please!")
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-01 14:16:41 -07:00
|
|
|
func (store Store) sliceFromJSONDecoder(dec *json.Decoder) ([]interface{}, error) {
|
|
|
|
|
var slice []interface{}
|
|
|
|
|
for {
|
|
|
|
|
t, err := dec.Token()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return slice, err
|
|
|
|
|
}
|
|
|
|
|
if delim, ok := t.(json.Delim); ok && delim.String() == "]" {
|
|
|
|
|
return slice, nil
|
2016-09-08 13:21:32 -07:00
|
|
|
} else if ok && delim.String() == "{" {
|
|
|
|
|
item, err := store.treeBranchFromJSONDecoder(dec)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return slice, err
|
|
|
|
|
}
|
|
|
|
|
slice = append(slice, item)
|
|
|
|
|
} else {
|
|
|
|
|
slice = append(slice, t)
|
2016-09-01 14:16:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var errEndOfObject = fmt.Errorf("End of object")
|
|
|
|
|
|
|
|
|
|
func (store Store) treeItemFromJSONDecoder(dec *json.Decoder) (sops.TreeItem, error) {
|
|
|
|
|
var item sops.TreeItem
|
|
|
|
|
key, err := dec.Token()
|
Don't consider io.EOF returned by Decoder.Token as error
[`Decoder.Token`](https://golang.org/pkg/encoding/json/#Decoder.Token)
returns nil, io.EOF at the input stream.
This caused the output json to have no "data" key for an input
containing a number:
```
{
"sops": {
"kms": null,
"gcp_kms": null,
"lastmodified": "2018-01-14T14:51:51Z",
"mac": "ENC[AES256_GCM,data:miI91EH0VGqTY9DuJweV61++dq1LmdBwbU/tkaznCeVo2H7z0vws0FdDJiKUiyCwd+PYkpklinVyGWzxDjgR1yWch+9uU4zFkwSiNwLTdQRitYE9Kwxd37E7+AFmJtZIfIdUZsx/gFP4YZ4Pn2cgVK6n9sNRyaGhR4PyCp7TXT4=,iv:XnyghTNLba1edrVYk8sum38pe736T3L5yGJMmBocDyE=,tag:b3z730u8+hPiNxmg8REFHg==,type:str]",
"pgp": [
{
"created_at": "2018-01-14T14:51:51Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcFMA90gOM45xlRNARAAj8AtDWZakRBpMmqRH3z6F+hIkyt2xpP911MAHpU1e4ma\nNZfUcKJybg2XFbAj40uDSEE1o1+hebU18nzYVwVUiDKBGN5f3rSgAIgtcK8u9JT2\nhRPndP7wkFK1t1+n3ne40ZotdqYefCLjHUalmS8Ka5wYDXGD9fOR3zBoaJ1VFWYu\nZyOltpqK76AFZ8dJkBBXcZCKfmZ2h2C9/tfSq5Hjibzddd/zit09zXsyHE6McFJU\n3YPGmGQ/kE+/1vkELIF3suGy7yB3Um0cRCEVnHoZJkE+lRZtxKKJ91oKLOfwJkoT\nOAHmeRJxDE45eae/wbWS4KHUFJ2IvfnUuaNCVrnYyzRP05wFxAuZI7XcV3ckVfaM\nBW2GkAUESfY9zYkTm/lOpUhAjEpqzjG+lSCt9VdHMMqOl8N4z6U5qzznm1ZL4Wf9\nbEV0zRc5XECmM6yjx7KHA8ivjdgxpKY9HgBI5ZkfjgoORfOaZaiVdteRmEOQM3yS\nWN+QTt4dkcfsqdpmYyHbCatgV9rsZdcIHS1kZ4EK7HMKzwR9+caRFA+o3NOm0hyx\nbNnMldVFr771KFoneau13A5HdZGdZRO7qMfpVZjdDQ8dFR1xtAimeoSGqIv5rcT3\n8UzrnNuSkHlPZHNgBloV5DoFLtWzd9VZCOl1KyLQLsSqQgbi1mbZlAQWfdWbwqHS\n4AHk3ef1I8MjQxVJFD4jSgC80OHzIeBK4C/heu/gfuKjuYWI4MLlPiuN6e+yoFT+\nR75GX1GgqTWP52gwxstEibTQ7n9zl6/gUeQ1/T+QOFDfajpawb8+xxyx4kjOzPnh\nU4MA\n=VSfw\n-----END PGP MESSAGE-----",
"fp": "C8F69F5F7059C32B3328DFE48BE9D15D0B0D06EB"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.0.0"
}
}
```
After the change:
```
{
"data": "ENC[AES256_GCM,data:PVw=,iv:cCDbWu1jdYkCIUcF/BtZGBs6mSWtdTI5ZF/A/i7RxIY=,tag:sFtal0nSo2koPDxnaKxLgA==,type:str]",
"sops": {
"kms": null,
"gcp_kms": null,
"lastmodified": "2018-01-14T14:52:38Z",
"mac": "ENC[AES256_GCM,data:BOyvRlaMKIGRcNOnmBGnN/Qz7i/l6Lhl5lx1OJ1VMb6nhuKkhCySktGVYOElUTgLc3CDKLfELNKiID2i6HKAkSAWQyYC1tIPAQTcBtnVd2Pt7Adzz8i8JFzWT+sc5rKLCOljnXwcXsxbmhrWwfQFj57wVWkvZTRNLfNZkcMnykw=,iv:JxCILR1qxAk391tTmLf/hXlr1L/JQWqhLbFHYR04HjI=,tag:0LSWWPWEBeK1Gm2mi4UBNA==,type:str]",
"pgp": [
{
"created_at": "2018-01-14T14:52:38Z",
"enc": "-----BEGIN PGP MESSAGE-----\n\nwcFMA90gOM45xlRNARAAR+FmJBwY4gnpUUZSwWRrwJ+PLhBzVXoIfZ2zqhk+gkvl\nGZyi62mCM5ZVoVPf8Pw+a9cQi+IzJHgKEOT/6PIp1chw7DhnoGbFJExcE3inniou\nlLo2pFTRH5jTvCE3yIIR/l1b9VMrtnOcZuYx9SobLjIv4wKtY1gMkbmrG2IDLmMT\n9QYM/MT/aCcUA/u6bYz8+ZjAS7NUEifji/SkZkYlL1tdCEdiHU/Cl4gScOhqcIsK\nGWGa+1jnxnYOmYq3FDMb90RSPgU8xkzl9EQIInF2t4K3Zj7E+9J93Y7N2udpNGah\nebPwxS4VTCLl1p69q9+nhO8rn9ySjeTjIoJwPoNDzWVowQEmUPZCTz+A28RKi02l\nGsuYxCzD5aF8cRhZbLk882fCAo77U9TiYYa00cq8kTqnCpZmtp3BevrdWTswJwin\n/TID0DOflahzj7iUP8MAVI2nzGosmCWEFiVONWq2l2z7ND78Y65G/d5hM3zv71U/\n1z0B8zzxZSbFFTV2YjADwWdizpeXJFJuVdynCQdPxt8qfNZVXcQfIHmYwh4M3k3U\nv5yV491mwCPNAJAoBaNJoKLnXx3ae4Aic4s2sF3V+AKK6rNiWtuAWsyjuwzmTse2\ntQisNTez5m+6r5seC7YvC2i9Vb2DNzqYn4M/13tHjxpPrNxdCGNneM1FKG8a03nS\n4AHk5BBSjhL9oc/o7zf8AsoFdeE2A+BR4KnhNXbg2eJlJCnO4IvlzVv3wYVwmh5W\nfyHqIQAX3ICb43o6Vo2/AGANvM5BdA3gX+ToyYFHsSMttWLz4zAUJWe04pRx0/rh\n73EA\n=0ON2\n-----END PGP MESSAGE-----",
"fp": "C8F69F5F7059C32B3328DFE48BE9D15D0B0D06EB"
}
],
"unencrypted_suffix": "_unencrypted",
"version": "3.0.0"
}
}
```
Fixes #235
2018-01-14 20:41:37 +05:30
|
|
|
if err != nil && err != io.EOF {
|
2016-09-01 14:16:41 -07:00
|
|
|
return item, err
|
|
|
|
|
}
|
|
|
|
|
if k, ok := key.(string); ok {
|
|
|
|
|
item.Key = k
|
|
|
|
|
} else if d, ok := key.(json.Delim); ok && d.String() == "}" {
|
|
|
|
|
return item, errEndOfObject
|
|
|
|
|
} else {
|
|
|
|
|
return item, fmt.Errorf("Expected JSON object key, got %s of type %T instead", key, key)
|
|
|
|
|
}
|
|
|
|
|
value, err := dec.Token()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return item, err
|
|
|
|
|
}
|
|
|
|
|
if delim, ok := value.(json.Delim); ok {
|
|
|
|
|
if delim.String() == "[" {
|
|
|
|
|
v, err := store.sliceFromJSONDecoder(dec)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return item, err
|
|
|
|
|
}
|
|
|
|
|
item.Value = v
|
|
|
|
|
}
|
|
|
|
|
if delim.String() == "{" {
|
|
|
|
|
v, err := store.treeBranchFromJSONDecoder(dec)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return item, err
|
|
|
|
|
}
|
|
|
|
|
item.Value = v
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
item.Value = value
|
|
|
|
|
}
|
|
|
|
|
return item, nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (store Store) treeBranchFromJSONDecoder(dec *json.Decoder) (sops.TreeBranch, error) {
|
|
|
|
|
var tree sops.TreeBranch
|
|
|
|
|
for {
|
|
|
|
|
item, err := store.treeItemFromJSONDecoder(dec)
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
return tree, nil
|
|
|
|
|
}
|
|
|
|
|
if err == errEndOfObject {
|
|
|
|
|
return tree, nil
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return tree, err
|
|
|
|
|
}
|
|
|
|
|
tree = append(tree, item)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-09 17:56:35 +01:00
|
|
|
func (store Store) encodeValue(v interface{}) ([]byte, error) {
|
2016-09-01 14:16:41 -07:00
|
|
|
switch v := v.(type) {
|
|
|
|
|
case sops.TreeBranch:
|
2016-11-09 17:56:35 +01:00
|
|
|
return store.encodeTree(v)
|
2016-10-26 16:57:00 +02:00
|
|
|
case []interface{}:
|
2016-11-09 17:56:35 +01:00
|
|
|
return store.encodeArray(v)
|
2016-09-01 14:16:41 -07:00
|
|
|
default:
|
|
|
|
|
return json.Marshal(v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-09 17:56:35 +01:00
|
|
|
func (store Store) encodeArray(array []interface{}) ([]byte, error) {
|
|
|
|
|
out := "["
|
|
|
|
|
for i, item := range array {
|
|
|
|
|
if _, ok := item.(sops.Comment); ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
v, err := store.encodeValue(item)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
out += string(v)
|
|
|
|
|
if i != len(array)-1 {
|
|
|
|
|
out += ","
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out += "]"
|
|
|
|
|
return []byte(out), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (store Store) encodeTree(tree sops.TreeBranch) ([]byte, error) {
|
|
|
|
|
out := "{"
|
2016-09-01 14:16:41 -07:00
|
|
|
for i, item := range tree {
|
2016-10-26 16:57:00 +02:00
|
|
|
if _, ok := item.Key.(sops.Comment); ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-11-09 17:56:35 +01:00
|
|
|
v, err := store.encodeValue(item.Value)
|
2016-09-01 14:16:41 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Error encoding value %s: %s", v, err)
|
|
|
|
|
}
|
2018-06-01 12:47:27 -07:00
|
|
|
k, err := json.Marshal(item.Key.(string))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Error encoding key %s: %s", k, err)
|
|
|
|
|
}
|
|
|
|
|
out += string(k) + `: ` + string(v)
|
2016-09-01 14:16:41 -07:00
|
|
|
if i != len(tree)-1 {
|
2016-11-09 17:56:35 +01:00
|
|
|
out += ","
|
2016-09-01 14:16:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-09 17:56:35 +01:00
|
|
|
return []byte(out + "}"), nil
|
2016-09-01 14:16:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (store Store) jsonFromTreeBranch(branch sops.TreeBranch) ([]byte, error) {
|
2016-11-09 17:56:35 +01:00
|
|
|
out, err := store.encodeTree(branch)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return store.reindentJSON(out)
|
2016-09-01 14:16:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (store Store) treeBranchFromJSON(in []byte) (sops.TreeBranch, error) {
|
|
|
|
|
dec := json.NewDecoder(bytes.NewReader(in))
|
|
|
|
|
dec.Token()
|
|
|
|
|
return store.treeBranchFromJSONDecoder(dec)
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-09 17:56:35 +01:00
|
|
|
func (store Store) reindentJSON(in []byte) ([]byte, error) {
|
|
|
|
|
var out bytes.Buffer
|
|
|
|
|
err := json.Indent(&out, in, "", "\t")
|
|
|
|
|
return out.Bytes(), err
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// LoadEncryptedFile loads an encrypted secrets file onto a sops.Tree object
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store *Store) LoadEncryptedFile(in []byte) (sops.Tree, error) {
|
|
|
|
|
// 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.
|
|
|
|
|
metadataHolder := stores.SopsFile{}
|
|
|
|
|
err := json.Unmarshal(in, &metadataHolder)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err, ok := err.(*json.UnmarshalTypeError); ok {
|
|
|
|
|
if err.Value == "number" && err.Struct == "Metadata" && err.Field == "version" {
|
|
|
|
|
return sops.Tree{},
|
|
|
|
|
fmt.Errorf("SOPS versions higher than 2.0.10 can not automatically decrypt JSON files " +
|
|
|
|
|
"created with SOPS 1.x. In order to be able to decrypt this file, you can either edit it " +
|
|
|
|
|
"manually and make sure the JSON value under `sops -> version` is a string and not a " +
|
|
|
|
|
"number, or you can rotate the file's key with any version of SOPS between 2.0 and 2.0.10 " +
|
|
|
|
|
"using `sops -r your_file.json`")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sops.Tree{}, fmt.Errorf("Error unmarshalling input json: %s", err)
|
|
|
|
|
}
|
|
|
|
|
if metadataHolder.Metadata == nil {
|
|
|
|
|
return sops.Tree{}, sops.MetadataNotFound
|
|
|
|
|
}
|
|
|
|
|
metadata, err := metadataHolder.Metadata.ToInternal()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return sops.Tree{}, err
|
|
|
|
|
}
|
|
|
|
|
// After that, we load the whole file into a map.
|
2016-09-01 14:16:41 -07:00
|
|
|
branch, err := store.treeBranchFromJSON(in)
|
2016-08-22 14:04:31 -07:00
|
|
|
if err != nil {
|
2018-04-20 10:13:46 +02:00
|
|
|
return sops.Tree{}, fmt.Errorf("Could not unmarshal input data: %s", err)
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
2018-04-20 10:13:46 +02:00
|
|
|
// Discard metadata, as we already loaded it.
|
2016-08-22 14:04:31 -07:00
|
|
|
for i, item := range branch {
|
|
|
|
|
if item.Key == "sops" {
|
|
|
|
|
branch = append(branch[:i], branch[i+1:]...)
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 10:13:46 +02:00
|
|
|
return sops.Tree{
|
2018-09-20 11:18:34 -06:00
|
|
|
Branches: sops.TreeBranches{
|
|
|
|
|
branch,
|
|
|
|
|
},
|
2018-04-20 10:13:46 +02:00
|
|
|
Metadata: metadata,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// LoadPlainFile loads plaintext json file bytes onto a sops.TreeBranches object
|
2018-09-20 11:18:34 -06:00
|
|
|
func (store *Store) LoadPlainFile(in []byte) (sops.TreeBranches, error) {
|
2018-04-20 10:13:46 +02:00
|
|
|
branch, err := store.treeBranchFromJSON(in)
|
|
|
|
|
if err != nil {
|
2018-09-20 11:18:34 -06:00
|
|
|
return nil, fmt.Errorf("Could not unmarshal input data: %s", err)
|
2018-04-20 10:13:46 +02:00
|
|
|
}
|
2018-09-20 11:18:34 -06:00
|
|
|
return sops.TreeBranches{
|
|
|
|
|
branch,
|
|
|
|
|
}, nil
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitEncryptedFile returns the encrypted bytes of the json file corresponding to a
|
|
|
|
|
// sops.Tree runtime object
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store *Store) EmitEncryptedFile(in sops.Tree) ([]byte, error) {
|
2018-09-20 11:18:34 -06:00
|
|
|
tree := append(in.Branches[0], sops.TreeItem{Key: "sops", Value: stores.MetadataFromInternal(in.Metadata)})
|
2016-09-01 14:16:41 -07:00
|
|
|
out, err := store.jsonFromTreeBranch(tree)
|
2016-08-22 14:04:31 -07:00
|
|
|
if err != nil {
|
2016-08-29 08:57:45 -07:00
|
|
|
return nil, fmt.Errorf("Error marshaling to json: %s", err)
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
2016-08-29 08:57:45 -07:00
|
|
|
return out, nil
|
2016-08-12 16:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitPlainFile returns the plaintext bytes of the json file corresponding to a
|
|
|
|
|
// sops.TreeBranches runtime object
|
2018-09-20 11:18:34 -06:00
|
|
|
func (store *Store) EmitPlainFile(in sops.TreeBranches) ([]byte, error) {
|
|
|
|
|
out, err := store.jsonFromTreeBranch(in[0])
|
2016-08-22 14:04:31 -07:00
|
|
|
if err != nil {
|
2016-08-29 08:57:45 -07:00
|
|
|
return nil, fmt.Errorf("Error marshaling to json: %s", err)
|
2016-08-22 14:04:31 -07:00
|
|
|
}
|
2016-08-29 08:57:45 -07:00
|
|
|
return out, nil
|
2016-08-15 17:16:37 -07:00
|
|
|
}
|
|
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitValue returns bytes corresponding to a single encoded value
|
|
|
|
|
// in a generic interface{} object
|
2018-04-20 10:13:46 +02:00
|
|
|
func (store *Store) EmitValue(v interface{}) ([]byte, error) {
|
2016-11-18 14:52:13 +01:00
|
|
|
s, err := store.encodeValue(v)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return store.reindentJSON(s)
|
|
|
|
|
}
|
2019-01-23 10:51:47 +01:00
|
|
|
|
2019-07-08 15:32:33 -07:00
|
|
|
// EmitExample returns the bytes corresponding to an example complex tree
|
2019-01-23 10:51:47 +01:00
|
|
|
func (store *Store) EmitExample() []byte {
|
|
|
|
|
bytes, err := store.EmitPlainFile(stores.ExampleComplexTree.Branches)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return bytes
|
|
|
|
|
}
|