Пофиксил получение имени файла с unicode
This commit is contained in:
parent
ea937b12f3
commit
54c6b9a28e
|
|
@ -16,10 +16,18 @@ public class App {
|
|||
private static final String LOG_CONTEXT = App.class.getSimpleName();
|
||||
private List<ContainerMetadata> containers;
|
||||
private final Console console;
|
||||
private DeviceContext deviceContext;
|
||||
private Device device;
|
||||
private final static String BACKUP_FOLDER = "backups";
|
||||
|
||||
private File backupFolder;
|
||||
|
||||
public App() {
|
||||
|
||||
backupFolder = new File(BACKUP_FOLDER);
|
||||
|
||||
if(!backupFolder.exists()){
|
||||
backupFolder.mkdir();
|
||||
}
|
||||
|
||||
console = new Console();
|
||||
try {
|
||||
|
|
@ -94,7 +102,7 @@ public class App {
|
|||
backupName = suggestion;
|
||||
}
|
||||
|
||||
var outputFile = new File(backupName + ".tar.gz");
|
||||
var outputFile = new File(backupFolder,backupName + ".tar.gz");
|
||||
|
||||
|
||||
List<FileMetadata> files;
|
||||
|
|
@ -104,7 +112,9 @@ public class App {
|
|||
throw new RuntimeException("Error loading container file tree", e);
|
||||
}
|
||||
|
||||
stopContainer(container);
|
||||
if(console.confirm("Container can be stopped now. Stop it?")) {
|
||||
stopContainer(container);
|
||||
}
|
||||
container = validate(container);
|
||||
SystemLogger.message("Building image layer from remote files", LOG_CONTEXT);
|
||||
createSystemLayer(container, files, layerFile);
|
||||
|
|
@ -120,14 +130,16 @@ public class App {
|
|||
layerFile.delete();
|
||||
}
|
||||
SystemLogger.message("Backup is done ", LOG_CONTEXT);
|
||||
console.pause();
|
||||
if (!container.isRunning() && console.confirm("Do you want to start container?")) {
|
||||
startContainer(container);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void stopContainer(ContainerMetadata container) {
|
||||
SystemLogger.message("Stopping container " + container, LOG_CONTEXT);
|
||||
try {
|
||||
deviceContext.getApiConnection().execute("/container/stop .id=" + container.getId());
|
||||
device.getApiConnection().execute("/container/stop .id=" + container.getId());
|
||||
Thread.sleep(10000L);
|
||||
} catch (MikrotikApiException | InterruptedException e) {
|
||||
throw new RuntimeException("Error stopping container", e);
|
||||
|
|
@ -140,8 +152,18 @@ public class App {
|
|||
if (!container.isRunning()) {
|
||||
SystemLogger.warning("The selected container is not running. There is a limitation that requires the container to be running.", LOG_CONTEXT);
|
||||
console.pause();
|
||||
startContainer(container);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void startContainer(ContainerMetadata container) {
|
||||
try {
|
||||
if (!container.isRunning()) {
|
||||
SystemLogger.message("Starting container " + container, LOG_CONTEXT);
|
||||
deviceContext.getApiConnection().execute("/container/start .id=" + container.getId());
|
||||
device.getApiConnection().execute("/container/start .id=" + container.getId());
|
||||
Thread.sleep(10000L);
|
||||
updateContainers();
|
||||
var id = container.getId();
|
||||
|
|
@ -164,12 +186,12 @@ public class App {
|
|||
}
|
||||
|
||||
private void updateContainers() throws MikrotikApiException {
|
||||
containers = deviceContext.getContainers();
|
||||
containers = device.getContainers();
|
||||
}
|
||||
|
||||
private void createSystemLayer(ContainerMetadata container, List<FileMetadata> files, File outputFile) {
|
||||
try {
|
||||
var sftp = deviceContext.getSftpClient();
|
||||
var sftp = device.getSftpClient();
|
||||
try (var outputStream = new FileOutputStream(outputFile)) {
|
||||
try (var tar = new TarArchiveOutputStream(outputStream)) {
|
||||
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
|
|
@ -223,7 +245,7 @@ public class App {
|
|||
SystemLogger.message("Reading container filesystem metadata...", LOG_CONTEXT);
|
||||
var systemDirs = List.of("/sys/", "/proc/", "/dev/", "/mnt/", "/run/", "/tmp/");
|
||||
var filterFiles = List.of("/.type");
|
||||
var files = deviceContext.listFilesystem(container).stream().filter(fileMetadata -> !filterFiles.contains(fileMetadata.getPath())).filter(f -> systemDirs.contains(f.getPath()) || systemDirs.stream().noneMatch(s -> f.getPath().startsWith(s))).toList();
|
||||
var files = device.listFilesystem(container).stream().filter(fileMetadata -> !filterFiles.contains(fileMetadata.getPath())).filter(f -> systemDirs.contains(f.getPath()) || systemDirs.stream().noneMatch(s -> f.getPath().startsWith(s))).toList();
|
||||
SystemLogger.message("Found " + files.size() + " files", LOG_CONTEXT);
|
||||
return files;
|
||||
}
|
||||
|
|
@ -238,16 +260,16 @@ public class App {
|
|||
|
||||
console.clear();
|
||||
|
||||
deviceContext = new DeviceContext(host, username, password);
|
||||
deviceContext.getApiConnection();
|
||||
device = new Device(host, username, password);
|
||||
device.getApiConnection();
|
||||
} catch (Exception e) {
|
||||
SystemLogger.error("Unable to connect", LOG_CONTEXT, e);
|
||||
if (deviceContext != null) {
|
||||
deviceContext.close();
|
||||
if (device != null) {
|
||||
device.close();
|
||||
}
|
||||
deviceContext = null;
|
||||
device = null;
|
||||
}
|
||||
} while (deviceContext == null);
|
||||
} while (device == null);
|
||||
|
||||
SystemLogger.message("Connected", LOG_CONTEXT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,13 +69,14 @@ public class Console implements Closeable {
|
|||
if (values.size() > 1) {
|
||||
do {
|
||||
clear();
|
||||
System.out.println(caption + ":");
|
||||
System.out.println(caption);
|
||||
var i = 0;
|
||||
for (var row : values) {
|
||||
System.out.println(String.valueOf(++i) + ' ' + row);
|
||||
}
|
||||
index = -1;
|
||||
|
||||
System.out.print("Select item: ");
|
||||
try {
|
||||
index = Integer.parseInt(read()) - 1;
|
||||
} catch (NumberFormatException ignored) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,16 @@ import ru.kirillius.utils.logging.SystemLogger;
|
|||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DeviceContext implements Closeable {
|
||||
public class Device implements Closeable {
|
||||
|
||||
private final static String LOG_CONTEXT = DeviceContext.class.getSimpleName();
|
||||
private final static String LOG_CONTEXT = Device.class.getSimpleName();
|
||||
private final static String LIST_COMMAND = "ls --color=never -lAnpR --full-time /";
|
||||
private final static String BEGIN_PATTERN = "///BEGIN-OF-STREAM///";
|
||||
private final static String END_PATTERN = "///END-OF-STREAM///";
|
||||
private final String host;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
|
@ -24,10 +28,12 @@ public class DeviceContext implements Closeable {
|
|||
private SSHClient sshClient;
|
||||
private ApiConnection apiConnection;
|
||||
|
||||
public DeviceContext(String host, String username, String password) {
|
||||
public Device(String host, String username, String password) {
|
||||
this.host = host;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public SFTPClient getSftpClient() throws IOException {
|
||||
|
|
@ -42,6 +48,14 @@ public class DeviceContext implements Closeable {
|
|||
return getApiConnection().execute("/container/print").stream().map(ContainerMetadata::new).toList();
|
||||
}
|
||||
|
||||
private String bytesToString(List<Byte> list) {
|
||||
var bytes = new byte[list.size()];
|
||||
for (var i = 0; i < list.size(); i++) {
|
||||
bytes[i] = list.get(i);
|
||||
}
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
private String stripBashSlashes(String path) {
|
||||
var escaped = false;
|
||||
var doubleQuotes = false;
|
||||
|
|
@ -55,6 +69,35 @@ public class DeviceContext implements Closeable {
|
|||
return path;
|
||||
}
|
||||
|
||||
if (path.contains("'$'")) {
|
||||
var builder = new StringBuilder();
|
||||
var first = true;
|
||||
for (var part : path.split(Pattern.quote("'$'"))) {
|
||||
if (first) {
|
||||
first = false;
|
||||
builder.append(part);
|
||||
continue;
|
||||
}
|
||||
var group = part.split(Pattern.quote("''"));
|
||||
if (group.length != 2) {
|
||||
SystemLogger.warning("Unable to parse file path: " + path, LOG_CONTEXT);
|
||||
builder.append(part);
|
||||
} else {
|
||||
|
||||
|
||||
var encoded = group[0];
|
||||
var bytes = new ArrayList<Byte>();
|
||||
for (var code : Arrays.stream(encoded.split(Pattern.quote("\\"))).skip(1).toArray(String[]::new)) {
|
||||
bytes.add((byte) Integer.parseInt(code, 8));
|
||||
}
|
||||
builder.append(bytesToString(bytes));
|
||||
builder.append(group[1]);
|
||||
}
|
||||
}
|
||||
path = builder.toString();
|
||||
}
|
||||
|
||||
|
||||
path = path.substring(1, path.length() - 1);
|
||||
|
||||
if (doubleQuotes) {
|
||||
|
|
@ -69,11 +112,6 @@ public class DeviceContext implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
private final static String LIST_COMMAND = "ls --color=never -lAnpR --full-time /";
|
||||
private final static String BEGIN_PATTERN = ":::BEGIN-OF-STREAM:::";
|
||||
private final static String END_PATTERN = ":::END-OF-STREAM:::";
|
||||
|
||||
|
||||
public List<FileMetadata> listFilesystem(ContainerMetadata container) throws IOException, InterruptedException {
|
||||
var files = new ArrayList<FileMetadata>();
|
||||
|
||||
|
|
@ -102,6 +140,10 @@ public class DeviceContext implements Closeable {
|
|||
if (line.endsWith(END_PATTERN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if ((line.startsWith("/") || line.startsWith("'/")) && line.endsWith(":")) {
|
||||
directory = line.substring(0, line.length() - 1);
|
||||
if (directory.indexOf('\'') == 0) {
|
||||
|
|
@ -8,6 +8,7 @@ public class DownloadStatus {
|
|||
private long startTime;
|
||||
private String label;
|
||||
private long lastUpdate = 0;
|
||||
private long lastBytes = 0;
|
||||
|
||||
public DownloadStatus(String label, long total) {
|
||||
this.label = label;
|
||||
|
|
@ -56,7 +57,8 @@ public class DownloadStatus {
|
|||
return;
|
||||
}
|
||||
clear();
|
||||
var speed = bytes * 1000 / (System.currentTimeMillis() - startTime);
|
||||
var speed = (bytes - lastBytes) * 1000 / (System.currentTimeMillis() - lastUpdate);
|
||||
lastBytes = bytes;
|
||||
buffer = ("[" + spinner() + "] " + label + " " + Math.floor((float) copied / total * 1000f) / 10 + "% " + copied + "/" + total + " (" + formatBytes(bytes) + ") " + formatBytes(speed) + "/s");
|
||||
System.out.print(buffer);
|
||||
lastUpdate = System.currentTimeMillis();
|
||||
|
|
@ -72,6 +74,7 @@ public class DownloadStatus {
|
|||
public void finish() {
|
||||
lastUpdate = 0;
|
||||
update();
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue