From d46ddab639561a6bdfae72e560bb2b1c4e1dd9bf Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Mon, 14 Jan 2019 22:52:30 +0000
Subject: [PATCH] Auto-install borg...

---
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/RunningMode.java          |    4 +
 borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java          |   14 ++++
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java     |  109 ++++++++++++++++++++++++++++++++++++
 borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx                 |    4 
 borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java |   35 +++++++++++
 5 files changed, 162 insertions(+), 4 deletions(-)

diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
index 68fbdcf..996f873 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/config/Configuration.java
@@ -22,7 +22,9 @@
     private static final String RESTORE_DIRNAME = "restore";
 
     @Getter
-    private String[][] binaries = {
+    private String binariesDownloadUrl = "https://github.com/borgbackup/borg/releases/download/1.1.8/";
+    @Getter
+    private String[][] borgBinaries = {
             {"freebsd64", "FreeBSD 64"},
             {"linux32", "Linux 32"},
             {"linux64", "Linux 64"},
@@ -32,7 +34,17 @@
     @Setter(AccessLevel.PACKAGE)
     private File workingDir;
 
+    /**
+     * One of the values "macosx64", "linux64" etc. for using a binary provided by BorgButler or null / "manual" for
+     * using a manual installed borg version.
+     */
     @Getter
+    private String borgBinary;
+    /**
+     * The path of the borg command to use.
+     */
+    @Getter
+    @Setter
     private String borgCommand = "borg";
     /**
      * Default is 100 MB (approximately).
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
new file mode 100644
index 0000000..46b1490
--- /dev/null
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
@@ -0,0 +1,109 @@
+package de.micromata.borgbutler.server;
+
+import de.micromata.borgbutler.BorgCommands;
+import de.micromata.borgbutler.config.Configuration;
+import de.micromata.borgbutler.config.ConfigurationHandler;
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+
+public class BorgInstallation {
+    private Logger log = LoggerFactory.getLogger(BorgInstallation.class);
+    private static final BorgInstallation instance = new BorgInstallation();
+
+    public static BorgInstallation getInstance() {
+        return instance;
+    }
+
+    public void initialize() {
+        Configuration configuration = ConfigurationHandler.getConfiguration();
+        String version = BorgCommands.version();
+        if (version != null) {
+            log.info("Using borg '" + configuration.getBorgCommand() + "', version: " + version);
+            return;
+        }
+        String[] binary = getBinary(RunningMode.getOSType());
+        download(binary);
+    }
+
+    private String[] getBinary(RunningMode.OSType osType) {
+        String os = null;
+        switch (osType) {
+            case MAC_OS:
+                os = "mac";
+                break;
+            case LINUX:
+                os = "linux64";
+                break;
+            case FREEBSD:
+                os = "freebsd64";
+                break;
+        }
+        if (os == null) {
+            return null;
+        }
+        for (String[] binary : ConfigurationHandler.getConfiguration().getBorgBinaries()) {
+            if (binary[0].contains(os)) {
+                return binary;
+            }
+        }
+        return null;
+    }
+
+    File download(RunningMode.OSType osType) {
+        String[] binary = getBinary(osType);
+        if (binary == null) {
+            log.info("Can't download binary (no binary found for OS '" + osType + "'.");
+            return null;
+        }
+        return download(binary);
+    }
+
+    private File download(String[] binary) {
+        String url = ConfigurationHandler.getConfiguration().getBinariesDownloadUrl() + "borg-" + binary[0];
+        log.info("Trying to download borg binary '" + binary[0] + "' (" + binary[1] + ") from url: " + url + "...");
+        HttpClientBuilder builder = HttpClients.custom()
+                .setDefaultRequestConfig(RequestConfig.custom()
+                        .setCookieSpec(CookieSpecs.STANDARD).build());
+        try (CloseableHttpClient httpClient = builder.build()) {
+            HttpGet getRequest = new HttpGet(url);
+
+            HttpResponse response = httpClient.execute(getRequest);
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new RuntimeException("Failed : HTTP error code : "
+                        + response.getStatusLine().getStatusCode());
+            }
+            File file = new File(getBinaryDir(), "borg-" + binary[0]);
+            FileUtils.copyInputStreamToFile(response.getEntity().getContent(), file);
+            log.info("Downloaded: " + file.getAbsolutePath());
+            file.setExecutable(true, false);
+            return file;
+        } catch (IOException ex) {
+            log.error(ex.getMessage(), ex);
+            return null;
+        }
+    }
+
+    private File getBinaryDir() {
+        File dir = new File(ConfigurationHandler.getInstance().getWorkingDir(), "bin");
+        if (!dir.exists()) {
+            log.info("Creating binary directory: " + dir.getAbsolutePath());
+            dir.mkdirs();
+        }
+        return dir;
+    }
+
+    private BorgInstallation() {
+    }
+}
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/RunningMode.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/RunningMode.java
index a3940ef..3da4739 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/RunningMode.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/RunningMode.java
@@ -16,7 +16,7 @@
 
     public enum UserManagement {SINGLE}
 
-    public enum OSType {MAC_OS, WINDOWS, LINUX, OTHER}
+    public enum OSType {MAC_OS, WINDOWS, LINUX, FREEBSD, OTHER}
 
     private static boolean running;
     private static File baseDir;
@@ -49,6 +49,8 @@
                 osType = OSType.WINDOWS;
             } else if (osTypeString.toLowerCase().contains("linux")) {
                 osType = OSType.LINUX;
+            } else if (osTypeString.toLowerCase().contains("freebsd")) {
+                osType = OSType.FREEBSD;
             } else {
                 osType = OSType.OTHER;
             }
diff --git a/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java b/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
new file mode 100644
index 0000000..f898948
--- /dev/null
+++ b/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
@@ -0,0 +1,35 @@
+package de.micromata.borgbutler.server;
+
+import de.micromata.borgbutler.config.ConfigurationHandler;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class BorgInstallationTest {
+    private Logger log = LoggerFactory.getLogger(BorgInstallationTest.class);
+
+    @Test
+    void foo() throws Exception {
+        ConfigurationHandler.getConfiguration().setBorgCommand("hurzel");
+        BorgInstallation borgInstallation = BorgInstallation.getInstance();
+        borgInstallation.initialize();
+    }
+
+    @Test
+    void downloadTest() {
+        checkDownload(RunningMode.OSType.LINUX, "borg-linux64");
+        checkDownload(RunningMode.OSType.MAC_OS, "borg-macosx64");
+        checkDownload(RunningMode.OSType.FREEBSD, "borg-freebsd64");
+        assertNull(BorgInstallation.getInstance().download(RunningMode.OSType.WINDOWS));
+    }
+
+    private void checkDownload(RunningMode.OSType osType, String expectedName) {
+        File file = BorgInstallation.getInstance().download(osType);
+        assertEquals(expectedName, file.getName());
+        assertTrue(file.canExecute());
+    }
+}
diff --git a/borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx b/borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx
index 3143c7e..b38500e 100644
--- a/borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx
+++ b/borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx
@@ -124,9 +124,9 @@
                                 value={this.state.binary}
                                 name={'binary'}
                                 onChange={this.handleTextChange}
-                                hint={'Choose your OS and BorgButler will download and use a ready to run borg binary from https://github.com/borgbackup/borg/releases or choose a manual installed version.'}
+                                hint={`Choose your OS and BorgButler will download and use a ready to run borg binary from ${this.state.binariesDownloadUrl} or choose a manual installed version.`}
                             >
-                                {this.state.binaries
+                                {this.state.borgBinaries
                                     .map((binary, index) => <FormOption label={binary[1]} value={binary[0]}
                                                                         key={index}/>)}
                                 <FormOption label={'Manual'} value={'manual'}/>

--
Gitblit v1.10.0