Repository: peopledoc/vault-cli

Issue:
vault get should be --text by default, --yaml if explicitely requested
This would make thing so much simpler.

@pilou- do you think it would change something to the role you developed ? (we can discuss this in a private space if you want)

Comments:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33edc55..3eaf296 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
 CHANGELOG
 =========
 
+0.8.0 (unreleased)
+------------------
+
+- vault get defaults to text for string secrets, yaml for complex types (#87)
+
 0.7.0 (2019-07-04)
 ------------------
 
diff --git a/README.md b/README.md
index 9656831..fb8bcf0 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ Options:
   --config-file PATH              Config file to use. Use 'no' to disable
                                   config file. Default value: first of
                                   ./.vault.yml, ~/.vault.yml, /etc/vault.yml
+  -V, --version
   -h, --help                      Show this message and exit.
 
 Commands:
@@ -85,17 +86,17 @@ $ vault --url=https://vault.mydomain:8200 --certificate=/etc/vault/certificate.k
 
 On the following examples, we'll be considering that we have a complete configuration file.
 
-### Read a secret (default is yaml format)
+### Read a secret in plain text (default)
 ```console
 $ vault get my_secret
---- qwerty
-...
+qwerty
 ```
 
-### Read a secret in plain text
+### Read a secret in yaml format
 ```console
-$ vault get my_secret --text
-qwerty
+$ vault get --yaml my_secret
+--- qwerty
+...
 ```
 
 ### Write a secret
@@ -109,9 +110,9 @@ Done
 $ export VAULT_CLI_BASE_PATH=myapp/
 $ vault set /global_secret sharedsecret
 Done
-$ vault get --text /global_secret
+$ vault get /global_secret
 sharedsecret
-$ vault get --text global_secret
+$ vault get global_secret
 Error: Secret not found
 $ unset VAULT_CLI_BASE_PATH
 ```
@@ -127,7 +128,7 @@ $ vault set third_secret --stdin
 <hit ctrl+d to end stdin>
 Done
 
-vault get --text third_secret
+vault get third_secret
 ----BEGIN SECRET KEY----
 ...
 ```
@@ -144,7 +145,7 @@ Done
 $ vault set -- -secret-name -oh-so-secret
 Done
 
-$ vault get --text -- -secret-name
+$ vault get -- -secret-name
 -oh-so-secret
 ```
 
@@ -159,6 +160,7 @@ Done
 $ vault set list_secret secret1 secret2 secret3
 Done
 
+# (For complex types, yaml format is selected)
 $ vault get list_secret
 ---
 - secret1
@@ -176,7 +178,7 @@ Error: Secret already exists at a. Use -f to force overwriting.
 $ vault --safe-write set -f a c
 Done
 ```
-(`safe-write` can be set in your configuration file, see below for details)
+(`safe-write` can be set in your configuration file, see details below)
 
 ### Get all values from the vault in a single command (yaml format)
 ```console
@@ -293,7 +295,7 @@ $ vault delete-all --force
 ```console
 $ vault set password foo
 $ vault set dsn '!template!proto://username:{{ vault("password") }}@host/'
-$ vault get --text dsn
+$ vault get dsn
 proto://username:foo@host/
 $ vault --no-render get --text dsn
 !template!proto://username:{{ vault("password") }}@host/
diff --git a/vault_cli/cli.py b/vault_cli/cli.py
index 2caf22b..0a311fa 100644
--- a/vault_cli/cli.py
+++ b/vault_cli/cli.py
@@ -204,11 +204,11 @@ def get_all(client_obj: client.VaultClientBase, path: Sequence[str]):
 @cli.command()
 @click.pass_obj
 @click.option(
-    "--text",
-    is_flag=True,
+    "--text/--yaml",
+    default=True,
     help=(
-        "--text implies --without-key. Returns the value in "
-        "plain text format instead of yaml."
+        "Returns the value in yaml format instead of plain text."
+        "If the secret is not a string, it will always be yaml."
     ),
 )
 @click.argument("name")
@@ -218,7 +218,10 @@ def get(client_obj: client.VaultClientBase, text: bool, name: str):
     Return a single secret value.
     """
     secret = client_obj.get_secret(path=name)
-    if text:
+    force_yaml = isinstance(secret, list) or isinstance(secret, dict)
+    if text and not force_yaml:
+        if secret is None:
+            secret = "null"
         click.echo(secret)
         return
 


Test patch:
diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py
index b8eaaa9..537da4b 100644
--- a/tests/integration/test_integration.py
+++ b/tests/integration/test_integration.py
@@ -26,6 +26,10 @@ def test_integration_cli(cli_runner, clean_vault):
 
     assert call(cli_runner, ["get", "a", "--text"]).output == "b\n"
 
+    assert call(cli_runner, ["get", "a"]).output == "b\n"
+
+    assert call(cli_runner, ["get", "a", "--yaml"]).output == "--- b\n...\n"
+
     # Both testing it and using it to clean the vault
     call(cli_runner, ["delete-all", "--force"])
 
@@ -37,7 +41,7 @@ def test_integration_cli(cli_runner, clean_vault):
 
     call(cli_runner, ["set", "c/d", "e"])
 
-    assert call(cli_runner, ["get", "c/d"]).output == "--- e\n...\n"
+    assert call(cli_runner, ["get", "c/d"]).output == "e\n"
 
     assert call(cli_runner, ["list"]).output == "a\nc/\n"
 
diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py
index ce6f235..469758e 100644
--- a/tests/unit/test_cli.py
+++ b/tests/unit/test_cli.py
@@ -82,20 +82,34 @@ def test_list(cli_runner, vault_with_token):
     assert result.exit_code == 0
 
 
-def test_get_text(cli_runner, vault_with_token):
+@pytest.mark.parametrize("extra_args", [["--text"], []])
+def test_get_text(cli_runner, vault_with_token, extra_args):
 
     vault_with_token.db = {"a": "bar"}
-    result = cli_runner.invoke(cli.cli, ["get", "a", "--text"])
+    result = cli_runner.invoke(cli.cli, ["get", "a"] + extra_args)
 
     assert result.output == "bar\n"
     assert result.exit_code == 0
 
 
+@pytest.mark.parametrize(
+    "input, output",
+    [([1, 2], "---\n- 1\n- 2\n"), ({"a": "b"}, "---\na: b\n"), (None, "null\n")],
+)
+def test_get_text_special_cases(cli_runner, vault_with_token, input, output):
+
+    vault_with_token.db = {"a": input}
+    result = cli_runner.invoke(cli.cli, ["get", "a"])
+
+    assert result.output == output
+    assert result.exit_code == 0
+
+
 def test_get_yaml(cli_runner, vault_with_token):
     vault_with_token.db = {"a": "bar"}
-    result = cli_runner.invoke(cli.cli, ["get", "a"])
+    result = cli_runner.invoke(cli.cli, ["get", "a", "--yaml"])
 
-    assert yaml.safe_load(result.output) == "bar"
+    assert result.output == "--- bar\n...\n"
     assert result.exit_code == 0