diff --git a/pom.xml b/pom.xml
index d0feba41ac..efdde98d7b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -234,7 +234,7 @@
software.amazon.awssdk.iotdevicesdk
aws-iot-device-sdk
- 1.19.1
+ 1.19.1-C2CUC-SNAPSHOT
org.bouncycastle
diff --git a/src/main/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgent.java b/src/main/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgent.java
index d593cd4e1f..8da7d73e52 100644
--- a/src/main/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgent.java
+++ b/src/main/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgent.java
@@ -69,6 +69,7 @@
public class ConfigStoreIPCEventStreamAgent {
private static final Logger logger = LogManager.getLogger(ConfigStoreIPCEventStreamAgent.class);
+ private static final String COMPONENT_NOT_FOUND_ERROR_FORMAT = "Component config not found for component %s";
private static final String KEY_NOT_FOUND_ERROR_MESSAGE = "Key not found";
private static final String SERVICE_NAME = "service-name";
@Getter(AccessLevel.PACKAGE)
@@ -192,7 +193,7 @@ public GetConfigurationResponse handleRequest(GetConfigurationRequest request) {
Topics serviceTopics = kernel.findServiceTopic(finalServiceName);
if (serviceTopics == null) {
- throw new ResourceNotFoundError(KEY_NOT_FOUND_ERROR_MESSAGE);
+ throw new ResourceNotFoundError(String.format(COMPONENT_NOT_FOUND_ERROR_FORMAT, finalServiceName));
}
Topics configTopics = serviceTopics.findInteriorChild(CONFIGURATION_CONFIG_KEY);
@@ -262,6 +263,9 @@ public UpdateConfigurationResponse handleRequest(UpdateConfigurationRequest requ
logger.atDebug().kv(SERVICE_NAME, serviceName).log("Config IPC config update request");
validateRequest(request);
+ String targetServiceName =
+ request.getComponentName() == null ? serviceName : request.getComponentName();
+
String[] keyPath = new String[0];
// Keypath is expected to denote the container node
if (request.getKeyPath() != null) {
@@ -270,7 +274,14 @@ public UpdateConfigurationResponse handleRequest(UpdateConfigurationRequest requ
Object value = request.getValueToMerge();
- Topics serviceTopics = kernel.findServiceTopic(serviceName);
+ logger.atDebug().kv(SERVICE_NAME, serviceName).log(
+ "Updating configuration for target component {}, key path {}, and value keys [{}]",
+ targetServiceName,
+ Arrays.toString(keyPath),
+ String.join(", ", request.getValueToMerge().keySet())
+ );
+
+ Topics serviceTopics = kernel.findServiceTopic(targetServiceName);
Topics configTopics = serviceTopics.lookupTopics(CONFIGURATION_CONFIG_KEY);
Node node = configTopics.findNode(keyPath);
long updateTime = request.getTimestamp().toEpochMilli();
@@ -327,14 +338,17 @@ private void validateRequest(UpdateConfigurationRequest request) {
+ restrictedConfigurationFields);
}
- Topics serviceTopics = kernel.findServiceTopic(serviceName);
- if (serviceTopics == null) {
- throw new InvalidArgumentsError("Component config not found for component " + serviceName);
+ final String targetServiceName =
+ request.getComponentName() == null ? serviceName : request.getComponentName();
+
+ Topics targetServiceTopics = kernel.findServiceTopic(targetServiceName);
+ if (targetServiceTopics == null) {
+ throw new InvalidArgumentsError(String.format(COMPONENT_NOT_FOUND_ERROR_FORMAT, targetServiceName));
}
- Topics configTopics = serviceTopics.lookupTopics(CONFIGURATION_CONFIG_KEY);
+ Topics configTopics = targetServiceTopics.lookupTopics(CONFIGURATION_CONFIG_KEY);
Node node = configTopics.findNode(keyPath);
if (node != null && !(node instanceof Topic) && !(node instanceof Topics)) {
- logger.atError().kv(SERVICE_NAME, serviceName)
+ logger.atError().kv(SERVICE_NAME, targetServiceName)
.log("Somehow Node has an unknown type {}", node.getClass());
throw new InvalidArgumentsError("Node corresponding to keypath "
+ request.getKeyPath().toString() + " has an unknown type");
diff --git a/src/test/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgentTest.java b/src/test/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgentTest.java
index 33bf563854..4bc10c1a33 100644
--- a/src/test/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgentTest.java
+++ b/src/test/java/com/aws/greengrass/builtin/services/configstore/ConfigStoreIPCEventStreamAgentTest.java
@@ -57,9 +57,11 @@
import static com.aws.greengrass.lifecyclemanager.GreengrassService.SERVICES_NAMESPACE_TOPIC;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -221,7 +223,7 @@ void GIVEN_get_config_request_WHEN_component_requested_does_not_exist_THEN_fail(
request.setKeyPath(Collections.singletonList("AnyKey"));
ResourceNotFoundError error = assertThrows(ResourceNotFoundError.class, () ->
agent.getGetConfigurationHandler(mockContext).handleRequest(request));
- assertEquals("Key not found", error.getMessage());
+ assertEquals("Component config not found for component WrongComponent", error.getMessage());
}
@Test
@@ -262,7 +264,21 @@ void GIVEN_agent_running_WHEN_update_config_request_for_ACL_THEN_update_fails()
request.setTimestamp(Instant.now());
Exception e = assertThrows(InvalidArgumentsError.class,
() -> agent.getUpdateConfigurationHandler(mockContext).handleRequest(request));
- assertTrue(e.getMessage().contains("Config update is not allowed for following fields"));
+ assertThat(e.getMessage(), containsString("Config update is not allowed for following fields"));
+ }
+
+ @Test
+ void GIVEN_component_name_provided_WHEN_update_config_request_for_ACL_THEN_update_fails() {
+ when(mockAuthenticationData.getIdentityLabel()).thenReturn(TEST_COMPONENT_A);
+ UpdateConfigurationRequest request = new UpdateConfigurationRequest();
+ request.setComponentName(TEST_COMPONENT_B);
+ request.setKeyPath(Collections.EMPTY_LIST);
+ request.setValueToMerge(Collections.singletonMap(ACCESS_CONTROL_NAMESPACE_TOPIC, Collections.singletonMap(
+ "aws.greengrass.ipc.pubsub", "policy")));
+ request.setTimestamp(Instant.now());
+ Exception e = assertThrows(InvalidArgumentsError.class,
+ () -> agent.getUpdateConfigurationHandler(mockContext).handleRequest(request));
+ assertThat(e.getMessage(), containsString("Config update is not allowed for following fields"));
}
@Test
@@ -274,7 +290,7 @@ void GIVEN_agent_running_WHEN_update_config_request_for_ACL_nested_node_THEN_upd
request.setTimestamp(Instant.now());
Exception e = assertThrows(InvalidArgumentsError.class,
() -> agent.getUpdateConfigurationHandler(mockContext).handleRequest(request));
- assertTrue(e.getMessage().contains("Config update is not allowed for following fields"));
+ assertThat(e.getMessage(), containsString("Config update is not allowed for following fields"));
}
@Test
@@ -293,6 +309,64 @@ void GIVEN_update_config_request_WHEN_update_key_does_not_exist_THEN_create_key(
assertEquals("SomeValue", newConfigKeyTopic.getOnce());
}
+ @Test
+ void GIVEN_update_config_request_WHEN_component_requested_does_not_exist_THEN_fail() {
+ when(mockAuthenticationData.getIdentityLabel()).thenReturn(TEST_COMPONENT_A);
+ String wrongComponentName = "WrongComponent";
+
+ UpdateConfigurationRequest request = new UpdateConfigurationRequest();
+ request.setComponentName(wrongComponentName);
+ request.setKeyPath(Collections.singletonList("AnyKeyPath"));
+ request.setValueToMerge(Collections.singletonMap("AnyKey", "AnyValue"));
+ request.setTimestamp(Instant.now());
+ InvalidArgumentsError error = assertThrows(InvalidArgumentsError.class, () ->
+ agent.getUpdateConfigurationHandler(mockContext).handleRequest(request));
+ assertEquals("Component config not found for component " + wrongComponentName, error.getMessage());
+ }
+
+ @Test
+ void GIVEN_component_name_provided_for_update_WHEN_key_path_does_not_exist_THEN_create_key_for_given_component() {
+ when(kernel.findServiceTopic(TEST_COMPONENT_B))
+ .thenReturn(configuration.getRoot().lookupTopics(SERVICES_NAMESPACE_TOPIC, TEST_COMPONENT_B));
+
+ assertNull(kernel.findServiceTopic(TEST_COMPONENT_B).findTopics(CONFIGURATION_CONFIG_KEY, "NewKey"));
+
+ when(mockAuthenticationData.getIdentityLabel()).thenReturn(TEST_COMPONENT_A);
+ UpdateConfigurationRequest request = new UpdateConfigurationRequest();
+ request.setKeyPath(Collections.singletonList("NewKey"));
+ request.setValueToMerge(Collections.singletonMap("NewValueToMergeKey", "SomeValue"));
+ request.setComponentName(TEST_COMPONENT_B);
+ request.setTimestamp(Instant.now());
+ UpdateConfigurationResponse response = agent.getUpdateConfigurationHandler(mockContext).handleRequest(request);
+ assertNotNull(response);
+
+ Topics newKeyTopics = kernel.findServiceTopic(TEST_COMPONENT_B).findTopics(CONFIGURATION_CONFIG_KEY, "NewKey");
+ assertNotNull(newKeyTopics);
+ Topic newValueToMergeTopic = kernel.findServiceTopic(TEST_COMPONENT_B).find(CONFIGURATION_CONFIG_KEY, "NewKey", "NewValueToMergeKey");
+ assertNotNull(newValueToMergeTopic);
+ assertEquals("SomeValue", newValueToMergeTopic.getOnce());
+ }
+
+ @Test
+ void GIVEN_component_name_provided_for_update_WHEN_key_not_provided_THEN_merge_values_at_config_root() {
+ when(kernel.findServiceTopic(TEST_COMPONENT_B))
+ .thenReturn(configuration.getRoot().lookupTopics(SERVICES_NAMESPACE_TOPIC, TEST_COMPONENT_B));
+
+ assertNull(kernel.findServiceTopic(TEST_COMPONENT_B).find(CONFIGURATION_CONFIG_KEY, "NewKey"));
+
+ when(mockAuthenticationData.getIdentityLabel()).thenReturn(TEST_COMPONENT_A);
+ UpdateConfigurationRequest request = new UpdateConfigurationRequest();
+ request.setValueToMerge(Collections.singletonMap("NewKey", "SomeValue"));
+ request.setComponentName(TEST_COMPONENT_B);
+ request.setTimestamp(Instant.now());
+ UpdateConfigurationResponse response = agent.getUpdateConfigurationHandler(mockContext).handleRequest(request);
+ assertNotNull(response);
+
+ Topic newConfigKeyTopic = kernel.findServiceTopic(TEST_COMPONENT_B).find(CONFIGURATION_CONFIG_KEY, "NewKey");
+ assertNotNull(newConfigKeyTopic);
+ assertEquals("SomeValue", newConfigKeyTopic.getOnce());
+ }
+
@Test
void GIVEN_update_config_request_WHEN_proposed_timestamp_is_stale_THEN_fail() {
when(mockAuthenticationData.getIdentityLabel()).thenReturn(TEST_COMPONENT_A);