|
33 | 33 | import java.util.Set; |
34 | 34 | import java.util.concurrent.CountDownLatch; |
35 | 35 | import java.util.concurrent.TimeUnit; |
| 36 | +import java.util.concurrent.atomic.AtomicBoolean; |
36 | 37 | import java.util.concurrent.atomic.AtomicInteger; |
37 | 38 | import java.util.concurrent.atomic.AtomicReference; |
38 | 39 |
|
@@ -66,6 +67,7 @@ public class ContainerIdentityIntegrationTest { |
66 | 67 | private static final String CONTAINER_ID = "container-id"; |
67 | 68 | private static final String SECOND_CONTAINER_ID = "container-id-2"; |
68 | 69 | private static final String UPDATED_CONTAINER_NAME = CONTAINER_NAME + "-v2"; |
| 70 | + private static final String INVALID_CONTAINER_NAME = "....@@@...."; |
69 | 71 |
|
70 | 72 | private ContainerInstance containerInstance; |
71 | 73 | private ContainerOrchestrationService containerOrchestrationService; |
@@ -213,14 +215,42 @@ public void clearsTemporaryPasswordAfterCleanupTemporaryIdentity() throws Except |
213 | 215 | thenTemporaryPasswordIsCleared(); |
214 | 216 | } |
215 | 217 |
|
| 218 | + @Test |
| 219 | + public void generatesValidTemporaryIdentityNameFromInvalidContainerName() throws Exception { |
| 220 | + givenIdentityIntegrationIsEnabled(INVALID_CONTAINER_NAME); |
| 221 | + givenTemporaryIdentityServiceProvidesPassword(); |
| 222 | + givenContainerOrchestratorStartsSuccessfully(); |
| 223 | + |
| 224 | + whenContainerInstanceIsActivated(); |
| 225 | + |
| 226 | + thenTemporaryIdentityUsesName("container_auto"); |
| 227 | + thenStartContainerReceivesTokenEnvironment(); |
| 228 | + } |
| 229 | + |
| 230 | + @Test |
| 231 | + public void retriesTemporaryIdentityCreationWhenNameAlreadyExists() throws Exception { |
| 232 | + givenIdentityIntegrationIsEnabled(); |
| 233 | + givenTemporaryIdentityServiceRetriesNameConflict(); |
| 234 | + givenContainerOrchestratorStartsSuccessfully(); |
| 235 | + |
| 236 | + whenContainerInstanceIsActivated(); |
| 237 | + |
| 238 | + thenTemporaryIdentityCreationIsRetriedWithSuffixedName(); |
| 239 | + thenStartContainerReceivesTokenEnvironment(); |
| 240 | + } |
| 241 | + |
216 | 242 | /* |
217 | 243 | * GIVEN |
218 | 244 | */ |
219 | 245 |
|
220 | 246 | private void givenIdentityIntegrationIsEnabled() { |
| 247 | + givenIdentityIntegrationIsEnabled(CONTAINER_NAME); |
| 248 | + } |
| 249 | + |
| 250 | + private void givenIdentityIntegrationIsEnabled(final String containerName) { |
221 | 251 | this.properties.put("container.enabled", true); |
222 | | - this.properties.put("container.name", CONTAINER_NAME); |
223 | | - this.properties.put("kura.service.pid", CONTAINER_NAME); |
| 252 | + this.properties.put("container.name", containerName); |
| 253 | + this.properties.put("kura.service.pid", containerName); |
224 | 254 | this.properties.put("container.image", CONTAINER_IMAGE); |
225 | 255 | this.properties.put("container.image.tag", CONTAINER_TAG); |
226 | 256 | this.properties.put("container.identity.enabled", true); |
@@ -269,6 +299,35 @@ private void givenContainerOrchestratorStartsSuccessfullyWithIds(final String... |
269 | 299 | }).when(this.containerOrchestrationService).startContainer(this.configurationCaptor.capture()); |
270 | 300 | } |
271 | 301 |
|
| 302 | + private void givenTemporaryIdentityServiceRetriesNameConflict() throws Exception { |
| 303 | + final AtomicBoolean conflictRaised = new AtomicBoolean(false); |
| 304 | + |
| 305 | + doAnswer(invocation -> { |
| 306 | + this.createCount.incrementAndGet(); |
| 307 | + |
| 308 | + final IdentityConfiguration config = invocation.getArgument(0); |
| 309 | + this.capturedIdentityNames.add(config.getName()); |
| 310 | + final char[] newPassword = config.getComponent(PasswordConfiguration.class).orElseThrow() |
| 311 | + .getNewPassword().orElseThrow(); |
| 312 | + this.capturedPasswords.add(new String(newPassword)); |
| 313 | + |
| 314 | + final String expectedBaseName = "container_" + CONTAINER_NAME.replace("-", "_"); |
| 315 | + if (!conflictRaised.get() && expectedBaseName.equals(config.getName())) { |
| 316 | + conflictRaised.set(true); |
| 317 | + throw new KuraException(KuraErrorCode.INVALID_PARAMETER, |
| 318 | + "An identity with name '" + config.getName() + "' already exists"); |
| 319 | + } |
| 320 | + |
| 321 | + return null; |
| 322 | + }).when(this.identityService).createTemporaryIdentity( |
| 323 | + this.identityConfigCaptor.capture(), this.durationCaptor.capture()); |
| 324 | + |
| 325 | + doAnswer(invocation -> { |
| 326 | + this.deleteCount.incrementAndGet(); |
| 327 | + return true; |
| 328 | + }).when(this.identityService).deleteIdentity(anyString()); |
| 329 | + } |
| 330 | + |
272 | 331 | private void givenTemporaryPasswordIsSet() throws Exception { |
273 | 332 | final Field field = ContainerInstance.class.getDeclaredField("currentTemporaryPassword"); |
274 | 333 | field.setAccessible(true); |
@@ -374,16 +433,28 @@ private void thenTemporaryIdentityIsCreatedWithPermissions() throws Exception { |
374 | 433 | permissions.stream().anyMatch(permission -> "rest.write".equals(permission.getName()))); |
375 | 434 | } |
376 | 435 |
|
| 436 | + private void thenTemporaryIdentityUsesName(final String expectedIdentityName) throws Exception { |
| 437 | + awaitCounterAtLeast(this.createCount, 1); |
| 438 | + |
| 439 | + verify(this.identityService).createTemporaryIdentity( |
| 440 | + this.identityConfigCaptor.capture(), this.durationCaptor.capture()); |
| 441 | + |
| 442 | + final IdentityConfiguration config = this.identityConfigCaptor.getValue(); |
| 443 | + assertEquals("Identity name should be normalized", expectedIdentityName, config.getName()); |
| 444 | + } |
| 445 | + |
377 | 446 | private void thenStartContainerReceivesTokenEnvironment() throws Exception { |
378 | 447 | assertTrue("Container start was not invoked", this.startLatch.await(2, TimeUnit.SECONDS)); |
379 | 448 |
|
380 | 449 | final ContainerConfiguration configuration = this.configurationCaptor.getValue(); |
381 | 450 | final List<String> envVars = configuration.getContainerEnvVars(); |
| 451 | + final String latestIdentityName = this.capturedIdentityNames.get(this.capturedIdentityNames.size() - 1); |
| 452 | + final String latestPassword = this.capturedPasswords.get(this.capturedPasswords.size() - 1); |
382 | 453 |
|
383 | 454 | assertTrue("Identity name var missing", |
384 | | - envVars.contains("KURA_IDENTITY_NAME=" + this.capturedIdentityNames.get(0))); |
| 455 | + envVars.contains("KURA_IDENTITY_NAME=" + latestIdentityName)); |
385 | 456 | assertTrue("Password var missing", |
386 | | - envVars.contains("KURA_IDENTITY_PASSWORD=" + this.capturedPasswords.get(0))); |
| 457 | + envVars.contains("KURA_IDENTITY_PASSWORD=" + latestPassword)); |
387 | 458 | // Check that KURA_REST_BASE_URL is set (now dynamic, not hardcoded to localhost:8080) |
388 | 459 | assertTrue("Base URL env var missing", |
389 | 460 | envVars.stream().anyMatch(envVar -> envVar.startsWith("KURA_REST_BASE_URL="))); |
@@ -424,6 +495,15 @@ private void thenLatestContainerStartReceivesRefreshedPassword() { |
424 | 495 | envVars.contains("KURA_IDENTITY_PASSWORD=" + latestPassword)); |
425 | 496 | } |
426 | 497 |
|
| 498 | + private void thenTemporaryIdentityCreationIsRetriedWithSuffixedName() throws Exception { |
| 499 | + awaitCounterAtLeast(this.createCount, 2); |
| 500 | + |
| 501 | + final String expectedBaseName = "container_" + CONTAINER_NAME.replace("-", "_"); |
| 502 | + assertEquals("First identity creation should use base name", expectedBaseName, this.capturedIdentityNames.get(0)); |
| 503 | + assertEquals("Second identity creation should use suffixed name", expectedBaseName + "_1", |
| 504 | + this.capturedIdentityNames.get(1)); |
| 505 | + } |
| 506 | + |
427 | 507 | private void thenTemporaryPasswordIsCleared() throws Exception { |
428 | 508 | final Field field = ContainerInstance.class.getDeclaredField("currentTemporaryPassword"); |
429 | 509 | field.setAccessible(true); |
|
0 commit comments