diff --git a/src/main/java/com/aws/greengrass/deployment/errorcode/DeploymentErrorCode.java b/src/main/java/com/aws/greengrass/deployment/errorcode/DeploymentErrorCode.java index ec604e9b33..4ee20370d4 100644 --- a/src/main/java/com/aws/greengrass/deployment/errorcode/DeploymentErrorCode.java +++ b/src/main/java/com/aws/greengrass/deployment/errorcode/DeploymentErrorCode.java @@ -64,7 +64,7 @@ public enum DeploymentErrorCode { // JVM hashing issue HASHING_ALGORITHM_UNAVAILABLE(DeploymentErrorType.DEVICE_ERROR), // Could be a local file issue or a Nucleus issue; we will categorize as the latter for visibility - LAUNCH_DIRECTORY_CORRUPTED(DeploymentErrorType.NUCLEUS_ERROR), + LAUNCH_DIRECTORY_CORRUPTED(DeploymentErrorType.DEVICE_ERROR), /* Component recipe errors */ RECIPE_PARSE_ERROR(DeploymentErrorType.COMPONENT_RECIPE_ERROR), diff --git a/src/main/java/com/aws/greengrass/lifecyclemanager/KernelAlternatives.java b/src/main/java/com/aws/greengrass/lifecyclemanager/KernelAlternatives.java index 3bffaaa754..7154de9782 100644 --- a/src/main/java/com/aws/greengrass/lifecyclemanager/KernelAlternatives.java +++ b/src/main/java/com/aws/greengrass/lifecyclemanager/KernelAlternatives.java @@ -169,10 +169,22 @@ public boolean isLaunchDirSetup() { * @throws DeploymentException when user is not allowed to change file permission */ public void validateLaunchDirSetupVerbose() throws DirectoryValidationException, DeploymentException { - Path currentDir = getCurrentDir(); - if (!Files.isSymbolicLink(currentDir)) { - throw new DirectoryValidationException("Missing symlink to current nucleus launch directory"); + if (!Files.isSymbolicLink(getCurrentDir())) { + try { + /* + Simply recreating a new init dir and relinking the current Nucleus jar path to it should be + sufficient as the kernel update workflow will create the new launch dir and its links anyway + */ + relinkInitLaunchDir(locateCurrentKernelUnpackDir(), true); + } catch (IOException ex) { + throw new DirectoryValidationException("Unable to relink init launch directory", ex); + } catch (URISyntaxException ex) { + // TODO: Fix usage of root path with spaces on linux + throw new DeploymentException("Could not parse init launch directory path", ex); + } } + + Path currentDir = getCurrentDir(); Path loaderPath = getLoaderPathFromLaunchDir(currentDir); if (Files.exists(loaderPath)) { if (!loaderPath.toFile().canExecute()) { diff --git a/src/main/java/com/aws/greengrass/lifecyclemanager/exceptions/DirectoryValidationException.java b/src/main/java/com/aws/greengrass/lifecyclemanager/exceptions/DirectoryValidationException.java index b4efa72735..8450719474 100644 --- a/src/main/java/com/aws/greengrass/lifecyclemanager/exceptions/DirectoryValidationException.java +++ b/src/main/java/com/aws/greengrass/lifecyclemanager/exceptions/DirectoryValidationException.java @@ -15,4 +15,9 @@ public DirectoryValidationException(String message) { super(message); super.addErrorCode(DeploymentErrorCode.LAUNCH_DIRECTORY_CORRUPTED); } + + public DirectoryValidationException(String message, Throwable throwable) { + super(message, throwable); + super.addErrorCode(DeploymentErrorCode.LAUNCH_DIRECTORY_CORRUPTED); + } } \ No newline at end of file diff --git a/src/test/java/com/aws/greengrass/deployment/activator/KernelUpdateActivatorTest.java b/src/test/java/com/aws/greengrass/deployment/activator/KernelUpdateActivatorTest.java index 3922d5e79f..e62fb994f3 100644 --- a/src/test/java/com/aws/greengrass/deployment/activator/KernelUpdateActivatorTest.java +++ b/src/test/java/com/aws/greengrass/deployment/activator/KernelUpdateActivatorTest.java @@ -227,7 +227,7 @@ void GIVEN_launch_dir_corrupted_WHEN_deployment_activate_THEN_deployment_fail(Ex assertEquals(mockException, result.getFailureCause().getCause()); List expectedStack = Arrays.asList("DEPLOYMENT_FAILURE", "LAUNCH_DIRECTORY_CORRUPTED"); - List expectedTypes = Collections.singletonList("NUCLEUS_ERROR"); + List expectedTypes = Collections.singletonList("DEVICE_ERROR"); TestUtils.validateGenerateErrorReport(result.getFailureCause(), expectedStack, expectedTypes); } diff --git a/src/test/java/com/aws/greengrass/lifecyclemanager/KernelAlternativesTest.java b/src/test/java/com/aws/greengrass/lifecyclemanager/KernelAlternativesTest.java index 2e7a67967a..77a6f6c444 100644 --- a/src/test/java/com/aws/greengrass/lifecyclemanager/KernelAlternativesTest.java +++ b/src/test/java/com/aws/greengrass/lifecyclemanager/KernelAlternativesTest.java @@ -8,6 +8,7 @@ import com.aws.greengrass.config.PlatformResolver; import com.aws.greengrass.deployment.DeploymentDirectoryManager; import com.aws.greengrass.deployment.bootstrap.BootstrapManager; +import com.aws.greengrass.lifecyclemanager.exceptions.DirectoryValidationException; import com.aws.greengrass.testcommons.testutilities.GGExtension; import com.aws.greengrass.util.NucleusPaths; import com.aws.greengrass.util.Utils; @@ -34,9 +35,12 @@ import static org.hamcrest.io.FileMatchers.anExistingFileOrDirectory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.internal.verification.VerificationModeFactory.times; @@ -212,6 +216,38 @@ void GIVEN_launch_params_THEN_write_to_file() throws Exception { assertEquals("mock string", new String(Files.readAllBytes(expectedLaunchParamsPath))); } + @Test + void GIVEN_validate_launch_dir_setup_WHEN_current_link_missing_and_exception_THEN_directory_validation_exception() throws IOException { + // GIVEN + Path outsidePath = createRandomDirectory(); + Path unpackPath = createRandomDirectory(); + Files.createDirectories(unpackPath.resolve("bin")); + String loaderName = "loader"; + if (PlatformResolver.isWindows) { + loaderName = "loader.cmd"; + } + Files.createFile(unpackPath.resolve("bin").resolve(loaderName)); + + Path distroPath = kernelAlternatives.getInitDir().resolve(KERNEL_DISTRIBUTION_DIR); + Files.createDirectories(kernelAlternatives.getInitDir()); + // current -> init + kernelAlternatives.setupLinkToDirectory(kernelAlternatives.getCurrentDir(), kernelAlternatives.getInitDir()); + // init/distro -> outsidePath + kernelAlternatives.setupLinkToDirectory(distroPath, outsidePath); + assertEquals(kernelAlternatives.getInitDir(), Files.readSymbolicLink(kernelAlternatives.getCurrentDir())); + assertEquals(outsidePath, Files.readSymbolicLink(distroPath)); + + // WHEN + Files.deleteIfExists(kernelAlternatives.getCurrentDir()); + lenient().doThrow(new IOException("Random test failure")) + .when(kernelAlternatives).relinkInitLaunchDir(any(Path.class), eq(true)); + + // THEN + DirectoryValidationException ex = assertThrows(DirectoryValidationException.class, + () -> kernelAlternatives.validateLaunchDirSetupVerbose()); + assertEquals(ex.getMessage(), "Unable to relink init launch directory"); + } + private Path createRandomDirectory() throws IOException { Path path = altsDir.resolve(Utils.generateRandomString(4)); Utils.createPaths(path);