fix(cli): don't throw on ns files migration for ns without files

closes https://github.com/kestra-io/kestra-ee/issues/6019#event-21325612781
This commit is contained in:
brian.mulier
2025-12-03 12:48:08 +01:00
committed by brian-mulier-p
parent 8f3a5058b1
commit 9d21ab4b26
3 changed files with 45 additions and 10 deletions

View File

@@ -98,10 +98,14 @@ public class MetadataMigrationService {
private static List<PathAndAttributes> listAllFromStorage(StorageInterface storage, Function<String, String> prefixFunction, String tenant, String namespace) throws IOException {
try {
String prefix = prefixFunction.apply(namespace);
if (!storage.exists(tenant, namespace, URI.create(StorageContext.KESTRA_PROTOCOL + prefix))) {
return Collections.emptyList();
}
return storage.allByPrefix(tenant, namespace, URI.create(StorageContext.KESTRA_PROTOCOL + prefix + "/"), true).stream()
.map(throwFunction(uri -> new PathAndAttributes(uri.getPath().substring(prefix.length()), storage.getAttributes(tenant, namespace, uri))))
.toList();
} catch (FileNotFoundException e) {
} catch (FileNotFoundException | NoSuchFileException e) {
return Collections.emptyList();
}
}

View File

@@ -131,6 +131,36 @@ public class NsFilesMetadataMigrationCommandTest {
}
}
@Test
void namespaceWithoutNsFile() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setOut(new PrintStream(out));
ByteArrayOutputStream err = new ByteArrayOutputStream();
System.setErr(new PrintStream(err));
try (ApplicationContext ctx = ApplicationContext.run(Environment.CLI, Environment.TEST)) {
String tenantId = TenantService.MAIN_TENANT;
String namespace = TestsUtils.randomNamespace();
// A flow is created from namespace 1, so the namespace files in this namespace should be migrated
FlowRepositoryInterface flowRepository = ctx.getBean(FlowRepositoryInterface.class);
flowRepository.create(GenericFlow.of(Flow.builder()
.tenantId(tenantId)
.id("a-flow")
.namespace(namespace)
.tasks(List.of(Log.builder().id("log").type(Log.class.getName()).message("logging").build()))
.build()));
String[] nsFilesMetadataMigrationCommand = {
"migrate", "metadata", "nsfiles"
};
PicocliRunner.call(App.class, ctx, nsFilesMetadataMigrationCommand);
assertThat(out.toString()).contains("✅ Namespace Files Metadata migration complete.");
assertThat(err.toString()).doesNotContain("java.nio.file.NoSuchFileException");
}
}
private static void putOldNsFile(StorageInterface storage, String namespace, String path, String value) throws IOException {
URI nsFileStorageUri = getNsFileStorageUri(namespace, path);
storage.put(TenantService.MAIN_TENANT, namespace, nsFileStorageUri, new StorageObject(

View File

@@ -13,6 +13,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.NoSuchFileException;
import java.util.List;
/**
@@ -52,7 +53,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return an InputStream to read the object's contents
* @throws IOException if the object cannot be read
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
InputStream get(String tenantId, @Nullable String namespace, URI uri) throws IOException;
/**
@@ -64,7 +65,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return an InputStream to read the object's contents
* @throws IOException if the object cannot be read
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
InputStream getInstanceResource(@Nullable String namespace, URI uri) throws IOException;
/**
@@ -76,7 +77,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return the storage object with metadata
* @throws IOException if the object cannot be retrieved
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
StorageObject getWithMetadata(String tenantId, @Nullable String namespace, URI uri) throws IOException;
/**
@@ -89,7 +90,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return a list of matching object URIs
* @throws IOException if the listing fails
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
List<URI> allByPrefix(String tenantId, @Nullable String namespace, URI prefix, boolean includeDirectories) throws IOException;
/**
@@ -101,7 +102,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return a list of file attributes
* @throws IOException if the listing fails
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
List<FileAttributes> list(String tenantId, @Nullable String namespace, URI uri) throws IOException;
/**
@@ -113,7 +114,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return a list of file attributes
* @throws IOException if the listing fails
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
List<FileAttributes> listInstanceResource(@Nullable String namespace, URI uri) throws IOException;
/**
@@ -159,7 +160,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return the file attributes
* @throws IOException if the attributes cannot be retrieved
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
FileAttributes getAttributes(String tenantId, @Nullable String namespace, URI uri) throws IOException;
/**
@@ -171,7 +172,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return the file attributes
* @throws IOException if the attributes cannot be retrieved
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
FileAttributes getInstanceAttributes(@Nullable String namespace, URI uri) throws IOException;
/**
@@ -288,7 +289,7 @@ public interface StorageInterface extends AutoCloseable, Plugin {
* @return the URI of the moved object
* @throws IOException if moving fails
*/
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class})
@Retryable(includes = {IOException.class}, excludes = {FileNotFoundException.class, NoSuchFileException.class})
URI move(String tenantId, @Nullable String namespace, URI from, URI to) throws IOException;
/**