Skip to content

Commit 95b8efa

Browse files
fix: allow TES restart on port changes (#1691)
* fix: allow TES restart on port changes * chore: fix unit tests --------- Co-authored-by: Siddhant Srivastava <sidsriv@amazon.com>
1 parent f7b3ca0 commit 95b8efa

File tree

3 files changed

+67
-28
lines changed

3 files changed

+67
-28
lines changed

src/integrationtests/java/com/aws/greengrass/integrationtests/e2e/tes/TESTest.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import com.aws.greengrass.logging.api.Logger;
2121
import com.aws.greengrass.logging.impl.LogManager;
2222
import com.aws.greengrass.tes.CredentialRequestHandler;
23+
import com.aws.greengrass.tes.HttpServerImpl;
2324
import com.aws.greengrass.tes.TokenExchangeService;
25+
import com.aws.greengrass.util.Coerce;
2426
import com.aws.greengrass.util.IamSdkClientFactory;
2527
import com.aws.greengrass.util.IotSdkClientFactory;
2628
import org.junit.jupiter.api.AfterAll;
@@ -43,16 +45,19 @@
4345
import java.nio.charset.StandardCharsets;
4446
import java.nio.file.Path;
4547
import java.util.Collections;
48+
import java.util.Objects;
4649
import java.util.UUID;
4750
import java.util.concurrent.CountDownLatch;
4851
import java.util.concurrent.TimeUnit;
52+
import java.util.concurrent.atomic.AtomicReference;
4953

5054
import static com.aws.greengrass.componentmanager.KernelConfigResolver.CONFIGURATION_CONFIG_KEY;
55+
import static com.aws.greengrass.deployment.DeviceConfiguration.IOT_ROLE_ALIAS_TOPIC;
5156
import static com.aws.greengrass.easysetup.DeviceProvisioningHelper.ThingInfo;
5257
import static com.aws.greengrass.integrationtests.e2e.BaseE2ETestCase.E2ETEST_ENV_STAGE;
5358
import static com.aws.greengrass.lifecyclemanager.GreengrassService.SERVICES_NAMESPACE_TOPIC;
5459
import static com.aws.greengrass.lifecyclemanager.GreengrassService.SETENV_CONFIG_NAMESPACE;
55-
import static com.aws.greengrass.deployment.DeviceConfiguration.IOT_ROLE_ALIAS_TOPIC;
60+
import static com.aws.greengrass.tes.TokenExchangeService.PORT_TOPIC;
5661
import static com.aws.greengrass.tes.TokenExchangeService.TES_URI_ENV_VARIABLE_NAME;
5762
import static com.aws.greengrass.tes.TokenExchangeService.TOKEN_EXCHANGE_SERVICE_TOPICS;
5863
import static com.aws.greengrass.testcommons.testutilities.ExceptionLogProtector.ignoreExceptionUltimateCauseOfType;
@@ -131,6 +136,43 @@ static void tearDown() throws URISyntaxException {
131136
}
132137
}
133138

139+
@Test
140+
void GIVEN_iot_role_alias_WHEN_port_changes_THEN_valid_credentials_are_returned_from_new_port() throws Exception {
141+
// Get current port and calculate new port
142+
int currentPort = Coerce.toInt(
143+
kernel.getConfig().lookupTopics(SERVICES_NAMESPACE_TOPIC, TOKEN_EXCHANGE_SERVICE_TOPICS)
144+
.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC));
145+
int newPort = currentPort + 1;
146+
String expectedUri = String.format("http://localhost:%d%s", newPort, HttpServerImpl.URL);
147+
148+
// Setup server restart detection
149+
CountDownLatch serverRestarted = new CountDownLatch(1);
150+
AtomicReference<String> urlString = new AtomicReference<>();
151+
kernel.getConfig().find(SETENV_CONFIG_NAMESPACE, TES_URI_ENV_VARIABLE_NAME).subscribe((why, newv) -> {
152+
urlString.set(Coerce.toString(newv));
153+
if (urlString.get().equals(expectedUri)) {
154+
serverRestarted.countDown();
155+
}
156+
});
157+
158+
// Change port and wait for server restart
159+
kernel.getConfig().lookupTopics(SERVICES_NAMESPACE_TOPIC, TOKEN_EXCHANGE_SERVICE_TOPICS)
160+
.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC).withValue(newPort);
161+
assertTrue(serverRestarted.await(5, TimeUnit.SECONDS), "Server did not restart within 5 seconds");
162+
assertEquals(expectedUri, urlString.get(), "New URL does not match expected URI");
163+
164+
// Get authentication token and make request
165+
String token = Objects.requireNonNull(kernel.getConfig()
166+
.findTopics(SERVICES_NAMESPACE_TOPIC, AuthenticationHandler.AUTHENTICATION_TOKEN_LOOKUP_KEY)).iterator()
167+
.next().getName();
168+
169+
String response = getResponseString(new URL(urlString.get()), token);
170+
171+
// Verify response format
172+
assertThat(response, matchesPattern(
173+
"\\{\"AccessKeyId\":\".+\",\"SecretAccessKey\":\".+\",\"Expiration\":\".+\",\"Token\":\".+\"\\}"));
174+
}
175+
134176
@Test
135177
void GIVEN_iot_role_alias_WHEN_tes_is_queried_THEN_valid_credentials_are_returned(ExtensionContext context)
136178
throws Exception {

src/main/java/com/aws/greengrass/tes/TokenExchangeService.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
2020

2121
import java.io.IOException;
22-
import java.util.Arrays;
22+
import java.util.Collections;
2323
import java.util.HashSet;
2424
import javax.inject.Inject;
2525

@@ -56,10 +56,19 @@ public TokenExchangeService(Topics topics,
5656
CredentialRequestHandler credentialRequestHandler,
5757
AuthorizationHandler authZHandler, DeviceConfiguration deviceConfiguration) {
5858
super(topics);
59-
// Port change should not be allowed
60-
topics.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC).dflt(DEFAULT_PORT)
61-
.subscribe((why, newv) -> port = Coerce.toInt(newv));
62-
59+
config.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC).dflt(DEFAULT_PORT);
60+
config.subscribe((why, node) -> {
61+
if (node != null && node.childOf(PORT_TOPIC)) {
62+
logger.atDebug("tes-config-change").kv("node", node).kv("why", why).log();
63+
port = Coerce.toInt(node);
64+
Topic activePortTopic = config.lookup(CONFIGURATION_CONFIG_KEY, ACTIVE_PORT_TOPIC);
65+
if (port != Coerce.toInt(activePortTopic)) {
66+
logger.atInfo("tes-config-change").kv(PORT_TOPIC, port).kv("node", node).kv("why", why)
67+
.log("Restarting TES server due to port config change");
68+
requestRestart();
69+
}
70+
}
71+
});
6372
deviceConfiguration.getIotRoleAlias().subscribe((why, newv) -> {
6473
iotRoleAlias = Coerce.toString(newv);
6574
});
@@ -72,7 +81,8 @@ public TokenExchangeService(Topics topics,
7281
public void postInject() {
7382
super.postInject();
7483
try {
75-
authZHandler.registerComponent(this.getName(), new HashSet<>(Arrays.asList(AUTHZ_TES_OPERATION)));
84+
authZHandler.registerComponent(this.getName(),
85+
new HashSet<>(Collections.singletonList(AUTHZ_TES_OPERATION)));
7686
} catch (AuthorizationException e) {
7787
serviceErrored(e);
7888
}

src/test/java/com/aws/greengrass/tes/TokenExchangeServiceTest.java

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.amazon.aws.iot.greengrass.component.common.ComponentType;
99
import com.aws.greengrass.authorization.AuthorizationHandler;
1010
import com.aws.greengrass.authorization.exceptions.AuthorizationException;
11+
import com.aws.greengrass.config.ChildChanged;
1112
import com.aws.greengrass.config.Configuration;
1213
import com.aws.greengrass.config.Subscriber;
1314
import com.aws.greengrass.config.Topic;
@@ -131,14 +132,7 @@ void cleanup() throws Exception {
131132
@ParameterizedTest
132133
@ValueSource(ints = {0, 3000})
133134
void GIVEN_token_exchange_service_WHEN_started_THEN_correct_env_set(int port) throws Exception {
134-
Topic portTopic = mock(Topic.class);
135-
when(portTopic.dflt(anyInt())).thenReturn(portTopic);
136-
when(portTopic.subscribe(any())).thenAnswer((a) -> {
137-
((Subscriber) a.getArgument(0)).published(WhatHappened.initialized, portTopic);
138-
return null;
139-
});
140-
when(portTopic.getOnce()).thenReturn(port);
141-
135+
Topic portTopic = Topic.of(new Context(), PORT_TOPIC, port);
142136
Topic roleTopic = mock(Topic.class);
143137
when(roleTopic.subscribe(any())).thenAnswer((a) -> {
144138
((Subscriber) a.getArgument(0)).published(WhatHappened.initialized, roleTopic);
@@ -157,6 +151,11 @@ void GIVEN_token_exchange_service_WHEN_started_THEN_correct_env_set(int port) th
157151
when(configuration.lookup(SERVICES_NAMESPACE_TOPIC, DEFAULT_NUCLEUS_COMPONENT_NAME, CONFIGURATION_CONFIG_KEY,
158152
IOT_ROLE_ALIAS_TOPIC)).thenReturn(roleTopic);
159153

154+
when(config.subscribe(any())).thenAnswer((a) -> {
155+
((ChildChanged) a.getArgument(0)).childChanged(WhatHappened.initialized, portTopic);
156+
return null;
157+
});
158+
160159
TokenExchangeService tes = new TokenExchangeService(config,
161160
mockCredentialHandler,
162161
mockAuthZHandler, deviceConfigurationWithRoleAlias(MOCK_ROLE_ALIAS));
@@ -180,7 +179,6 @@ void GIVEN_token_exchange_service_WHEN_started_THEN_correct_env_set(int port) th
180179
verify(mockAuthZHandler).registerComponent(stringArgumentCaptor.capture(), operationsCaptor.capture());
181180
assertEquals(TOKEN_EXCHANGE_SERVICE_TOPICS, stringArgumentCaptor.getValue());
182181
assertTrue(operationsCaptor.getValue().contains(TokenExchangeService.AUTHZ_TES_OPERATION));
183-
184182
}
185183

186184
@ParameterizedTest
@@ -202,13 +200,7 @@ void GIVEN_token_exchange_service_WHEN_started_with_empty_role_alias_THEN_server
202200
// set mock for port topic
203201
Topic portTopic = mock(Topic.class);
204202
when(portTopic.dflt(anyInt())).thenReturn(portTopic);
205-
when(portTopic.subscribe(any())).thenAnswer((a) -> {
206-
((Subscriber) a.getArgument(0)).published(WhatHappened.initialized, portTopic);
207-
return null;
208-
});
209-
when(portTopic.getOnce()).thenReturn(8080);
210-
211-
203+
212204
when(config.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC)).thenReturn(portTopic);
213205
when(configuration.lookup(SERVICES_NAMESPACE_TOPIC, DEFAULT_NUCLEUS_COMPONENT_NAME, CONFIGURATION_CONFIG_KEY,
214206
IOT_ROLE_ALIAS_TOPIC)).thenReturn(roleTopic);
@@ -240,11 +232,6 @@ void GIVEN_token_exchange_service_WHEN_auth_errors_THEN_server_errors_out(Extens
240232
// set mock for port topic
241233
Topic portTopic = mock(Topic.class);
242234
when(portTopic.dflt(anyInt())).thenReturn(portTopic);
243-
when(portTopic.subscribe(any())).thenAnswer((a) -> {
244-
((Subscriber) a.getArgument(0)).published(WhatHappened.initialized, portTopic);
245-
return null;
246-
});
247-
when(portTopic.getOnce()).thenReturn(8080);
248235
when(config.lookup(CONFIGURATION_CONFIG_KEY, PORT_TOPIC)).thenReturn(portTopic);
249236
when(configuration.lookup(SERVICES_NAMESPACE_TOPIC, DEFAULT_NUCLEUS_COMPONENT_NAME, CONFIGURATION_CONFIG_KEY,
250237
IOT_ROLE_ALIAS_TOPIC)).thenReturn(roleTopic);

0 commit comments

Comments
 (0)