From f8d5bf0842d18c71494c4e1b6d0257e3b6790c6d Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 3 Oct 2025 22:59:28 +0000 Subject: [PATCH 1/4] Add spec document for getconfiguration ipc --- .../ipc_commands/getconfiguration_ipc.md | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/spec/library/ipc_commands/getconfiguration_ipc.md diff --git a/docs/spec/library/ipc_commands/getconfiguration_ipc.md b/docs/spec/library/ipc_commands/getconfiguration_ipc.md new file mode 100644 index 000000000..68285b896 --- /dev/null +++ b/docs/spec/library/ipc_commands/getconfiguration_ipc.md @@ -0,0 +1,119 @@ +# `GetConfiguration` IPC command + +The `GetConfiguration` IPC command retrieves a component's configuration stored +at the nucleus level using the `gg_config` core-bus interface. + +- [get-config-1] It can be invoked with the topic `GetConfiguration`. +- [get-config-2] It can get any component's configurations under the `services` + section. +- [get-config-3] It does not require access control policy in recipe to work. +- [get-config-4] It cannot retrieve any values under the `system` section of + config. + +## Parameters + +- [get-config-params-1] `componentName` is an optional parameter of type buffer. + - [get-config-params-1.1] If not provided, the calling component's name is + used. +- [get-config-params-2] `keyPath` is an optional parameter of type list. + - [get-config-params-2.1] List elements are buffers containing a single level + in the key hierarchy. + - [get-config-params-2.2] If not provided, will assume the entire component + configuration was requested. + +## Response + +- [get-config-resp-1] On success, returns a map containing `componentName` and + `value` keys. + - [get-config-resp-1.1] For non-map values, the `value` contains the key + itself with the retrieved value. + - [get-config-resp-1.2] For map values, the `value` contains the map contents + without the key itself. +- [get-config-resp-2] On failure, returns a map containing `message`, + `_service`, `_message` and `_errorCode`. + - [get-config-resp-2.1] `ResourceNotFoundError` is returned if the key path + does not exist. + +## Examples + +Following are a few examples of how the request and response could look like in +different scenarios based on the configuration as follows: + +``` +"DefaultConfiguration": { + "test_str": "hi", + "sample_map": { + "key1": "value1", + "key2_list": [ + "subkey1", + "subkey2" + ], + "key10": 10.0123456, + "key4": null, + "key5_map": { + "subkey1":"subvalue1", + "subkey2":"subvalue2" + } + } +} +``` + +Case 1: Get Configuration key path with float + +- Request: `{"keyPath": ["sample_map", "key10"]}` +- Response: + `{"componentName":"com.example.ConfigUpdater","value":{"key10":10.0123456}}` + +Case 2: Get Configuration key path with list + +- Request: `{"keyPath": ["sample_map", "key2_list"]}` +- Response: + `{"componentName":"com.example.ConfigUpdater","value":{"key2_list":["subkey1","subkey2"]}}` + +Case 3: Get Configuration key path with map + +- Request: `{"keyPath": ["sample_map", "key5_map"]}` +- Response: + `{"componentName":"com.example.ConfigUpdater","value":{"subkey1":"subvalue1","subkey2":"subvalue2"}}` + + > Note that the key isn't included with the response + +Case 4: Get Configuration key path with string + +- Request: `{"keyPath": ["sample_map", "key5_map", "subkey1"]}` +- Response: + `{"componentName":"com.example.ConfigUpdater","value":{"subkey1":"subvalue1"}}` + +Case 5: Get Configuration key path with null + +- Request:`{"keyPath": ["sample_map", "key4"]}` +- Response:`{"componentName":"com.example.ConfigUpdater","value":{"key4":null}}` + +Case 6: Get Configuration key path does not exist. + +- Request: + `{"componentName": "com.example.ConfigUpdater", "keyPath": ["randomKey"]}` +- Response: + `{"message":"Key not found","_service":"aws.greengrass#GreengrassCoreIPC","_message":"Key not found","_errorCode":"ResourceNotFoundError"}` + +RAW: + +```shell +Packet from client 9: + Headers: + [:content-type] => String(StrBytes { bytes: b"application/json" }) + [service-model-type] => String(StrBytes { bytes: b"aws.greengrass#GetConfigurationRequest" }) + [:message-type] => Int32(0) + [:message-flags] => Int32(0) + [:stream-id] => Int32(1) + [operation] => String(StrBytes { bytes: b"aws.greengrass#GetConfiguration" }) + Value: {"componentName": "com.example.ConfigUpdater", "keyPath": ["randomKey"]} +Packet from server: + Headers: + [:content-type] => String(StrBytes { bytes: b"application/json" }) + [service-model-type] => String(StrBytes { bytes: b"aws.greengrass#ResourceNotFoundError" }) + [:message-type] => Int32(1) + [:message-flags] => Int32(2) + [:stream-id] => Int32(1) + Value: {"message":"Key not found","_service":"aws.greengrass#GreengrassCoreIPC","_message":"Key not found","_errorCode":"ResourceNotFoundError"} +``` From d80e6921b58859929f6b1b2f2897a63a09ec89c6 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Fri, 3 Oct 2025 23:00:15 +0000 Subject: [PATCH 2/4] Update getConfiguration per spec requirements --- .../src/services/config/get_configuration.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/ggipcd/src/services/config/get_configuration.c b/modules/ggipcd/src/services/config/get_configuration.c index 2be8854bc..667274a14 100644 --- a/modules/ggipcd/src/services/config/get_configuration.c +++ b/modules/ggipcd/src/services/config/get_configuration.c @@ -89,6 +89,30 @@ GglError ggl_handle_get_configuration( return ret; } + // According to the IPC spec, if keyPath has a valid value, + // For MAP values a map without the keyPath leaf is returned. + // For non-MAP values, a map with the keyPath leaf and the value is + // returned. + GglKV wrapped_result = { 0 }; + GglObjectType read_type = ggl_obj_type(read_value); + if (read_type != GGL_TYPE_MAP) { + if (key_path.len > 0) { + wrapped_result = ggl_kv( + ggl_obj_into_buf(key_path.items[key_path.len - 1]), read_value + ); + read_value + = ggl_obj_map((GglMap) { .pairs = &wrapped_result, .len = 1 }); + } else { + // A state where the whole configuration is requested but the result + // is not a map then error. + *ipc_error + = (GglIpcError) { .error_code = GGL_IPC_ERR_INVALID_ARGUMENTS, + .message = GGL_STR("Key is not valid.") }; + + return GGL_ERR_CONFIG; + } + } + return ggl_ipc_response_send( handle, stream_id, From b8894cb38d38765e315fcefca5380c79f07061da Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Mon, 6 Oct 2025 20:32:13 +0000 Subject: [PATCH 3/4] Add db_to_yaml script --- misc/db_to_yaml.sh | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 misc/db_to_yaml.sh diff --git a/misc/db_to_yaml.sh b/misc/db_to_yaml.sh new file mode 100755 index 000000000..714e6b62e --- /dev/null +++ b/misc/db_to_yaml.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then + echo "Usage: $0 " + echo "Convert Greengrass configuration database to YAML format" + echo "" + echo "Arguments:" + echo " Path to the configuration database file" + echo "" + echo "Options:" + echo " -h, --help Show this help message and exit" + exit 0 +fi + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +DB_PATH="$1" + +if [ ! -f "$DB_PATH" ]; then + echo "Error: Database file $DB_PATH not found" + exit 1 +fi + +# Create a temporary file for processing +TEMP_FILE=$(mktemp) + +# Function to process hierarchy recursively +process_node() { + local parent_id="$1" + local indent="$2" + sqlite3 "$DB_PATH" "SELECT k.keyvalue, k.keyid, v.value FROM keyTable k LEFT JOIN relationTable r ON k.keyid = r.keyid LEFT JOIN valueTable v ON k.keyid = v.keyid WHERE r.parentid = $parent_id ORDER BY k.keyid;" | while IFS='|' read -r keyvalue keyid value; do + child_count=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM relationTable WHERE parentid = $keyid;") + + if [ "$child_count" -gt 0 ]; then + echo "${indent}${keyvalue}:" >> "$TEMP_FILE" + process_node "$keyid" "$indent " + elif [ -n "$value" ]; then + clean_value=$(echo "$value" | sed 's/^"//; s/"$//') + if [[ "$clean_value" =~ ^[0-9]+$ ]]; then + echo "${indent}${keyvalue}: $clean_value" >> "$TEMP_FILE" + else + echo "${indent}${keyvalue}: \"$clean_value\"" >> "$TEMP_FILE" + fi + else + echo "${indent}${keyvalue}: {}" >> "$TEMP_FILE" + fi + done +} + +# Start output +echo "---" > "$TEMP_FILE" + +# Process root nodes +sqlite3 "$DB_PATH" "SELECT k.keyvalue, k.keyid, v.value FROM keyTable k LEFT JOIN relationTable r ON k.keyid = r.keyid LEFT JOIN valueTable v ON k.keyid = v.keyid WHERE r.parentid IS NULL ORDER BY k.keyid;" | while IFS='|' read -r keyvalue keyid value; do + child_count=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM relationTable WHERE parentid = $keyid;") + + if [ "$child_count" -gt 0 ]; then + echo "${keyvalue}:" >> "$TEMP_FILE" + process_node "$keyid" " " + elif [ -n "$value" ]; then + clean_value=$(echo "$value" | sed 's/^"//; s/"$//') + if [[ "$clean_value" =~ ^[0-9]+$ ]]; then + echo "${keyvalue}: $clean_value" >> "$TEMP_FILE" + else + echo "${keyvalue}: \"$clean_value\"" >> "$TEMP_FILE" + fi + else + echo "${keyvalue}: {}" >> "$TEMP_FILE" + fi +done + +# Output the result and clean up +cat "$TEMP_FILE" +rm "$TEMP_FILE" From 47d2c64890a12071901de9c03ddcd588e34f4372 Mon Sep 17 00:00:00 2001 From: Anubhav Rawal Date: Tue, 7 Oct 2025 23:52:33 +0000 Subject: [PATCH 4/4] Update sdk commit --- fc_deps.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fc_deps.json b/fc_deps.json index aee501c1b..e78f1eaa2 100644 --- a/fc_deps.json +++ b/fc_deps.json @@ -16,7 +16,7 @@ }, "ggl_sdk": { "url": "https://github.com/aws-greengrass/aws-greengrass-sdk-lite.git", - "rev": "9f9b95363d6de4c68ee5d34ba5c6065b4c1b31fb", - "hash": "sha256-tIFIKvZI10WDHBfdFBYAbgHVpNaoUUhzsO8tshk5o3A=" + "rev": "b83773450bccf6974cded2005792a2400ce5e018", + "hash": "sha256-2bvRk23DnGQe9DqbZBDDWt1JzikIbwD4fF8yFr6j+HE=" } }