Skip to content

Commit c1dadc6

Browse files
authored
Merge pull request #1894 from felixfontein/value-stdin
Add `--value-stdin` option to `sops set`
2 parents 5703e40 + d9be059 commit c1dadc6

File tree

3 files changed

+88
-6
lines changed

3 files changed

+88
-6
lines changed

README.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,17 @@ The value must be formatted as json.
15601560
15611561
$ sops set ~/git/svc/sops/example.yaml '["an_array"][1]' '{"uid1":null,"uid2":1000,"uid3":["bob"]}'
15621562
1563+
You can also provide the value from a file or stdin:
1564+
1565+
.. code:: sh
1566+
1567+
# Provide the value from a file
1568+
$ echo '{"uid1":null,"uid2":1000,"uid3":["bob"]}' > /tmp/example-value
1569+
$ sops set ~/git/svc/sops/example.yaml --value-file '["an_array"][1]' /tmp/example-value
1570+
1571+
# Provide the value from stdin
1572+
$ echo '{"uid1":null,"uid2":1000,"uid3":["bob"]}' | sops set ~/git/svc/sops/example.yaml --value-stdin '["an_array"][1]'
1573+
15631574
Unset a sub-part in a document tree
15641575
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15651576

cmd/sops/main.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
encodingjson "encoding/json"
66
"fmt"
7+
"io"
78
"net"
89
"net/url"
910
"os"
@@ -1374,8 +1375,8 @@ func main() {
13741375
},
13751376
{
13761377
Name: "set",
1377-
Usage: `set a specific key or branch in the input document. value must be a json encoded string. eg. '/path/to/file ["somekey"][0] {"somevalue":true}'`,
1378-
ArgsUsage: `file index value`,
1378+
Usage: `set a specific key or branch in the input document. value must be a JSON encoded string, for example '/path/to/file ["somekey"][0] {"somevalue":true}', or a path if --value-file is used, or omitted if --value-stdin is used`,
1379+
ArgsUsage: `file index [ value ]`,
13791380
Flags: append([]cli.Flag{
13801381
cli.StringFlag{
13811382
Name: "input-type",
@@ -1387,7 +1388,11 @@ func main() {
13871388
},
13881389
cli.BoolFlag{
13891390
Name: "value-file",
1390-
Usage: "treat 'value' as a file to read the actual value from (avoids leaking secrets in process listings)",
1391+
Usage: "treat 'value' as a file to read the actual value from (avoids leaking secrets in process listings). Mutually exclusive with --value-stdin",
1392+
},
1393+
cli.BoolFlag{
1394+
Name: "value-stdin",
1395+
Usage: "treat 'value' as a file to read the actual value from (avoids leaking secrets in process listings). Mutually exclusive with --value-file",
13911396
},
13921397
cli.IntFlag{
13931398
Name: "shamir-secret-sharing-threshold",
@@ -1411,8 +1416,17 @@ func main() {
14111416
if c.Bool("verbose") {
14121417
logging.SetLevel(logrus.DebugLevel)
14131418
}
1414-
if c.NArg() != 3 {
1415-
return common.NewExitError("Error: no file specified, or index and value are missing", codes.NoFileSpecified)
1419+
if c.Bool("value-file") && c.Bool("value-stdin") {
1420+
return common.NewExitError("Error: cannot use both --value-file and --value-stdin", codes.ErrorGeneric)
1421+
}
1422+
if c.Bool("value-stdin") {
1423+
if c.NArg() != 2 {
1424+
return common.NewExitError("Error: file specified, or index and value are missing. Need precisely 2 positional arguments since --value-stdin is used.", codes.NoFileSpecified)
1425+
}
1426+
} else {
1427+
if c.NArg() != 3 {
1428+
return common.NewExitError("Error: no file specified, or index and value are missing. Need precisely 3 positional arguments.", codes.NoFileSpecified)
1429+
}
14161430
}
14171431
fileName, err := filepath.Abs(c.Args()[0])
14181432
if err != nil {
@@ -1435,7 +1449,13 @@ func main() {
14351449
}
14361450

14371451
var data string
1438-
if c.Bool("value-file") {
1452+
if c.Bool("value-stdin") {
1453+
content, err := io.ReadAll(os.Stdin)
1454+
if err != nil {
1455+
return toExitError(err)
1456+
}
1457+
data = string(content)
1458+
} else if c.Bool("value-file") {
14391459
filename := c.Args()[2]
14401460
content, err := os.ReadFile(filename)
14411461
if err != nil {

functional-tests/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,57 @@ bar: baz",
538538
panic!("Output JSON does not have the expected structure");
539539
}
540540

541+
#[test]
542+
fn set_json_file_insert_with_value_stdin() {
543+
let file_path = prepare_temp_file(
544+
"test_set_json_file_insert_with_value_stdin.json",
545+
r#"{"a": 2, "b": "ba"}"#.as_bytes(),
546+
);
547+
assert!(
548+
Command::new(SOPS_BINARY_PATH)
549+
.arg("encrypt")
550+
.arg("-i")
551+
.arg(file_path.clone())
552+
.output()
553+
.expect("Error running sops")
554+
.status
555+
.success(),
556+
"sops didn't exit successfully"
557+
);
558+
// let value_file = prepare_temp_file("insert_value_file.json", r#"{"cc": "ccc"}"#.as_bytes());
559+
let process = Command::new(SOPS_BINARY_PATH)
560+
.arg("set")
561+
.arg("--value-stdin")
562+
.arg(file_path.clone())
563+
.arg(r#"["c"]"#)
564+
.stdin(Stdio::piped())
565+
.stdout(Stdio::piped())
566+
.spawn()
567+
.expect("Error running sops");
568+
write_to_stdin(&process, b"{\"cc\": \"ccc\"}");
569+
let output = process.wait_with_output().expect("Failed to wait on sops");
570+
println!(
571+
"stdout: {}, stderr: {}",
572+
String::from_utf8_lossy(&output.stdout),
573+
String::from_utf8_lossy(&output.stderr)
574+
);
575+
assert!(output.status.success(), "sops didn't exit successfully");
576+
let mut s = String::new();
577+
File::open(file_path)
578+
.unwrap()
579+
.read_to_string(&mut s)
580+
.unwrap();
581+
let data: Value = serde_json::from_str(&s).expect("Error parsing sops's JSON output");
582+
if let Value::Mapping(data) = data {
583+
let a = data.get(&Value::String("c".to_owned())).unwrap();
584+
if let &Value::Mapping(ref a) = a {
585+
assert_encrypted!(&a, Value::String("cc".to_owned()));
586+
return;
587+
}
588+
}
589+
panic!("Output JSON does not have the expected structure");
590+
}
591+
541592
#[test]
542593
fn set_yaml_file_update() {
543594
let file_path = prepare_temp_file(

0 commit comments

Comments
 (0)