diff --git a/doc/cmd/flux-kvs.adoc b/doc/cmd/flux-kvs.adoc index d8f2d7e39d6d..e78f7cdc6be3 100644 --- a/doc/cmd/flux-kvs.adoc +++ b/doc/cmd/flux-kvs.adoc @@ -45,14 +45,19 @@ COMMANDS Retrieve the value stored under 'key'. If nothing has been stored under 'key', display an error message. +*type* 'key' ['key...']:: +Display the JSON type of the value under key: 'null', 'boolean', 'double', +'int', 'object', 'array', or 'string'. If nothing has been stored under +'key', display an error message. + *put* 'key=value' ['key=value...']:: Store 'value' under 'key' and commit it. If it already has a value, overwrite it. -*dir* 'key':: +*dir* ['key']:: Display all keys and their values under the directory 'key'. If 'key' does not exist or is not a directory, display an error message. -If 'key' is ".", the entire KVS namespace is dumped. +If 'key' is not provided, "." (root of the namespace) is assumed. *unlink* 'key' ['key...']:: Remove 'key' from the KVS and commit the change. If 'key' represents diff --git a/doc/cmd/spell.en.pws b/doc/cmd/spell.en.pws index 0686f3af989e..00fc0c73d1f5 100644 --- a/doc/cmd/spell.en.pws +++ b/doc/cmd/spell.en.pws @@ -133,3 +133,4 @@ tty uri usefile secdir +boolean diff --git a/src/cmd/flux-kvs.c b/src/cmd/flux-kvs.c index 18b32fe722a6..5a2f0cc6fdfd 100644 --- a/src/cmd/flux-kvs.c +++ b/src/cmd/flux-kvs.c @@ -43,6 +43,7 @@ static const struct option longopts[] = { }; void cmd_get (flux_t h, int argc, char **argv); +void cmd_type (flux_t h, int argc, char **argv); void cmd_put (flux_t h, int argc, char **argv); void cmd_unlink (flux_t h, int argc, char **argv); void cmd_link (flux_t h, int argc, char **argv); @@ -63,6 +64,7 @@ void usage (void) { fprintf (stderr, "Usage: flux-kvs get key [key...]\n" +" flux-kvs type key [key...]\n" " flux-kvs put key=val [key=val...]\n" " flux-kvs unlink key [key...]\n" " flux-kvs link target link_name\n" @@ -72,7 +74,7 @@ void usage (void) " flux-kvs watch-dir key\n" " flux-kvs copy-tokvs key file\n" " flux-kvs copy-fromkvs key file\n" -" flux-kvs dir key\n" +" flux-kvs dir [key]\n" " flux-kvs version\n" " flux-kvs wait version\n" " flux-kvs dropcache\n" @@ -108,6 +110,8 @@ int main (int argc, char *argv[]) if (!strcmp (cmd, "get")) cmd_get (h, argc - optind, argv + optind); + else if (!strcmp (cmd, "type")) + cmd_type (h, argc - optind, argv + optind); else if (!strcmp (cmd, "put")) cmd_put (h, argc - optind, argv + optind); else if (!strcmp (cmd, "unlink")) @@ -144,6 +148,45 @@ int main (int argc, char *argv[]) return 0; } +void cmd_type (flux_t h, int argc, char **argv) +{ + JSON o; + int i; + + if (argc == 0) + msg_exit ("get-type: specify one or more keys"); + for (i = 0; i < argc; i++) { + if (kvs_get (h, argv[i], &o) < 0) + err_exit ("%s", argv[i]); + const char *type = "unknown"; + switch (json_object_get_type (o)) { + case json_type_null: + type = "null"; + break; + case json_type_boolean: + type = "boolean"; + break; + case json_type_double: + type = "double"; + break; + case json_type_int: + type = "int"; + break; + case json_type_object: + type = "object"; + break; + case json_type_array: + type = "array"; + break; + case json_type_string: + type = "string"; + break; + } + printf ("%s\n", type); + Jput (o); + } +} + void cmd_get (flux_t h, int argc, char **argv) { JSON o; @@ -154,8 +197,28 @@ void cmd_get (flux_t h, int argc, char **argv) for (i = 0; i < argc; i++) { if (kvs_get (h, argv[i], &o) < 0) err_exit ("%s", argv[i]); - else - printf ("%s\n", Jtostr (o)); + switch (json_object_get_type (o)) { + case json_type_null: + printf ("nil\n"); + break; + case json_type_boolean: + printf ("%s\n", json_object_get_boolean (o) ? "true" : "false"); + break; + case json_type_double: + printf ("%f\n", json_object_get_double (o)); + break; + case json_type_int: + printf ("%d\n", json_object_get_int (o)); + break; + case json_type_string: + printf ("%s\n", json_object_get_string (o)); + break; + case json_type_array: + case json_type_object: + default: + printf ("%s\n", Jtostr (o)); + break; + } Jput (o); } } @@ -360,11 +423,39 @@ void cmd_copy_fromkvs (flux_t h, int argc, char **argv) free (buf); } + +static void dump_kvs_val (const char *key, JSON o) +{ + switch (json_object_get_type (o)) { + case json_type_null: + printf ("%s = nil\n", key); + break; + case json_type_boolean: + printf ("%s = %s\n", key, json_object_get_boolean (o) + ? "true" : "false"); + break; + case json_type_double: + printf ("%s = %f\n", key, json_object_get_double (o)); + break; + case json_type_int: + printf ("%s = %d\n", key, json_object_get_int (o)); + break; + case json_type_string: + printf ("%s = %s\n", key, json_object_get_string (o)); + break; + case json_type_array: + case json_type_object: + default: + printf ("%s = %s\n", key, Jtostr (o)); + break; + } +} + static void dump_kvs_dir (flux_t h, const char *path) { kvsdir_t dir; kvsitr_t itr; - const char *name, *js; + const char *name; char *key; if (kvs_get_dir (h, &dir, "%s", path) < 0) @@ -375,7 +466,6 @@ static void dump_kvs_dir (flux_t h, const char *path) key = kvsdir_key_at (dir, name); if (kvsdir_issymlink (dir, name)) { char *link; - if (kvs_get_symlink (h, key, &link) < 0) err_exit ("%s", key); printf ("%s -> %s\n", key, link); @@ -383,21 +473,12 @@ static void dump_kvs_dir (flux_t h, const char *path) } else if (kvsdir_isdir (dir, name)) { dump_kvs_dir (h, key); - } else { - json_object *o; - int len, max; - + JSON o; if (kvs_get (h, key, &o) < 0) err_exit ("%s", key); - js = json_object_to_json_string_ext (o, JSON_C_TO_STRING_PLAIN); - len = strlen (js); - max = 80 - strlen (key) - 4; - if (len > max) - printf ("%s = %.*s ...\n", key, max - 4, js); - else - printf ("%s = %s\n", key, js); - json_object_put (o); + dump_kvs_val (key, o); + Jput (o); } free (key); } @@ -434,9 +515,12 @@ void cmd_watch_dir (flux_t h, int argc, char **argv) void cmd_dir (flux_t h, int argc, char **argv) { - if (argc != 1) - msg_exit ("dir: specify one directory"); - dump_kvs_dir (h, argv[0]); + if (argc == 0) + dump_kvs_dir (h, "."); + else if (argc == 1) + dump_kvs_dir (h, argv[0]); + else + msg_exit ("dir: specify zero or one directory"); } /* diff --git a/t/t1000-kvs-basic.t b/t/t1000-kvs-basic.t index 9d3548aa82b2..ebbea5cce910 100755 --- a/t/t1000-kvs-basic.t +++ b/t/t1000-kvs-basic.t @@ -27,6 +27,12 @@ test_kvs_key() { #fi } +test_kvs_type () { + flux kvs type "$1" >output + echo "$2" >expected + test_cmp output expected +} + test_expect_success 'kvs: get a nonexistent key' ' test_must_fail flux kvs get NOT.A.KEY ' @@ -35,6 +41,9 @@ test_expect_success 'kvs: get a nonexistent key' ' test_expect_success 'kvs: integer put' ' flux kvs put $KEY=42 ' +test_expect_success 'kvs: integer type' ' + test_kvs_type $KEY int +' test_expect_success 'kvs: integer get' ' test_kvs_key $KEY 42 ' @@ -44,46 +53,65 @@ test_expect_success 'kvs: unlink works' ' ' test_expect_success 'kvs: value can be empty' ' flux kvs put $KEY= && - test_kvs_key $KEY "\"\"" + test_kvs_key $KEY "" && + test_kvs_type $KEY string ' KEY=$TEST.b.c.d DIR=$TEST.b.c test_expect_success 'kvs: string put' ' flux kvs put $KEY="Hello world" ' -test_expect_success 'kvs: string get' ' - test_kvs_key $KEY "\"Hello world\"" +test_expect_success 'kvs: string type' ' + test_kvs_type $KEY string ' -test_expect_success 'kvs: unlink works' ' - flux kvs unlink $KEY && - test_must_fail flux kvs get $KEY +test_expect_success 'kvs: string get' ' + test_kvs_key $KEY "Hello world" ' test_expect_success 'kvs: boolean put (true)' ' flux kvs put $KEY=true ' +test_expect_success 'kvs: boolean type' ' + test_kvs_type $KEY boolean +' test_expect_success 'kvs: boolean get (true)' ' test_kvs_key $KEY true ' -test_expect_success 'kvs: unlink works' ' - flux kvs unlink $KEY && - test_must_fail flux kvs get $KEY -' test_expect_success 'kvs: boolean put (false)' ' flux kvs put $KEY=false ' +test_expect_success 'kvs: boolean type' ' + test_kvs_type $KEY boolean +' test_expect_success 'kvs: boolean get (false)' ' test_kvs_key $KEY false ' -test_expect_success 'kvs: unlink works' ' - flux kvs unlink $KEY && - test_must_fail flux kvs get $KEY -' test_expect_success 'kvs: put double' ' flux kvs put $KEY=3.14159 ' +test_expect_success 'kvs: double type' ' + test_kvs_type $KEY double +' test_expect_success 'kvs: get double' ' test_kvs_key $KEY 3.141590 ' +test_expect_success 'kvs: array put' ' + flux kvs put $KEY="[1,3,5,7]" +' +test_expect_success 'kvs: array type' ' + test_kvs_type $KEY array +' +test_expect_success 'kvs: array get' ' + test_kvs_key $KEY "[ 1, 3, 5, 7 ]" +' +test_expect_success 'kvs: object put' ' + flux kvs put $KEY="{\"a\":42}" +' +test_expect_success 'kvs: object type' ' + test_kvs_type $KEY object +' +test_expect_success 'kvs: object get' ' + test_kvs_key $KEY "{ \"a\": 42 }" +' test_expect_success 'kvs: try to retrieve key as directory should fail' ' test_must_fail flux kvs dir $KEY ' @@ -113,7 +141,7 @@ test_expect_success 'kvs: put values in a directory then retrieve them' ' $DIR.a = 69 $DIR.b = 70 $DIR.c = 3.140000 -$DIR.d = "snerg" +$DIR.d = snerg $DIR.e = true EOF test_cmp expected output @@ -126,7 +154,7 @@ test_expect_success 'kvs: create a dir with keys and subdir' ' $DIR.a = 69 $DIR.b = 70 $DIR.c.d.e.f.g = 3.140000 -$DIR.d = "snerg" +$DIR.d = snerg $DIR.e = true EOF test_cmp expected output @@ -140,7 +168,7 @@ test_expect_success 'kvs: directory with multiple subdirs' ' $DIR.a = 69 $DIR.b.c.d.e.f.g = 70 $DIR.c.a.b = 3.140000 -$DIR.d = "snerg" +$DIR.d = snerg $DIR.e = true EOF test_cmp expected output @@ -154,7 +182,7 @@ test_expect_success 'kvs: symlink: works' ' flux kvs put $TARGET=\"foo\" && flux kvs link $TARGET $TEST.Q && OUTPUT=$(flux kvs get $TEST.Q) && - test "$OUTPUT" = "\"foo\"" + test "$OUTPUT" = "foo" ' test_expect_success 'kvs: symlink: path resolution when intermediate component is a symlink' ' flux kvs unlink $TEST &&