mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
19.29.2022 9926a79503c9bd6454c076f2b60cd577f6061e4d
Using Borg version 1.1.17 as default. Version is now configurable via web client and json file.
1 files deleted
1 files added
1 files renamed
11 files modified
239 ■■■■■ changed files
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt 7 ●●●● patch | view | raw | blame | history
borgbutler-core/src/test/resources/log4j.properties 1 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgConfig.java 50 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java 61 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/ServerConfiguration.kt 4 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/ConfigurationInfo.kt 4 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/ConfigurationRest.kt 10 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/SystemInfo.kt 8 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/SystemInfoRest.kt 10 ●●●● patch | view | raw | blame | history
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgConfigTest.java 20 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java 17 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgVersionTest.java 20 ●●●●● patch | view | raw | blame | history
borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt 1 ●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx 26 ●●●●● patch | view | raw | blame | history
borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt
@@ -30,8 +30,6 @@
    /**
     * The borg version to install from github (optional).
     */
    // @JsonIgnore needed by client: ConfigurationserverTab.jsx fails otherwise (conflicting borgVersion fields).
    @JsonIgnore
    var borgVersion: String? = null
    /**
@@ -101,6 +99,7 @@
    fun copyFrom(other: Configuration) {
        borgCommand = other.borgCommand
        borgVersion = other.borgVersion
        maxArchiveContentCacheCapacityMb = other.maxArchiveContentCacheCapacityMb
        showDemoRepos = other.showDemoRepos
    }
@@ -123,6 +122,10 @@
        return this
    }
    override fun toString(): String {
        return "borgCommand=[$borgCommand], borgVersion=[$borgVersion], workingDir=[$workingDir], maxArchiveContentCacheCapacityMb=[$maxArchiveContentCacheCapacityMb], showDemoRepos=[$showDemoRepos]"
    }
    companion object {
        /**
         * Default dir name for restoring archives.
borgbutler-core/src/test/resources/log4j.properties
@@ -1,5 +1,4 @@
log4j.rootLogger=info, stdout
log4j.logger.de.micromata.paypal=debug
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgConfig.java
File was renamed from borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgVersion.java
@@ -2,12 +2,8 @@
import org.apache.commons.lang3.StringUtils;
public class BorgVersion {
    public static final String BORG_DEFAULT_DOWNLOAD_VERSION = "1.1.16";
    private static final String BORG_VERSION = BORG_DEFAULT_DOWNLOAD_VERSION;
    private String binariesDownloadVersion = BORG_DEFAULT_DOWNLOAD_VERSION;
public class BorgConfig {
    public static final String BORG_DEFAULT_DOWNLOAD_VERSION = "1.1.17";
    private String[][] borgBinaries = {
            {"freebsd64", "FreeBSD 64"},
@@ -18,7 +14,7 @@
    private String minimumRequiredBorgVersion = "1.1.8";
    public String getBinariesDownloadUrl() {
        return "https://github.com/borgbackup/borg/releases/download/" + binariesDownloadVersion + "/";
        return "https://github.com/borgbackup/borg/releases/download/" + version + "/";
    }
    /**
@@ -28,10 +24,10 @@
    private String borgBinary;
    private boolean versionOK = false;
    private String version;
    private String version = BORG_DEFAULT_DOWNLOAD_VERSION;
    private String statusMessage;
    public BorgVersion copyFrom(BorgVersion other) {
    public BorgConfig copyFrom(BorgConfig other) {
        this.borgBinary = other.borgBinary;
        this.versionOK = other.versionOK;
        this.version = other.version;
@@ -39,18 +35,6 @@
        return this;
    }
    public String getBinariesDownloadVersion() {
        return this.binariesDownloadVersion;
    }
    public void setBinariesDownloadVersion(String binariesDownloadVersion) {
        if (StringUtils.isNotBlank(binariesDownloadVersion)) {
            this.binariesDownloadVersion = binariesDownloadVersion;
        } else {
            this.binariesDownloadVersion = BORG_DEFAULT_DOWNLOAD_VERSION;
        }
    }
    public String[][] getBorgBinaries() {
        return this.borgBinaries;
    }
@@ -78,22 +62,26 @@
        return this.statusMessage;
    }
    BorgVersion setBorgBinary(String borgBinary) {
    BorgConfig setBorgBinary(String borgBinary) {
        this.borgBinary = borgBinary;
        return this;
    }
    BorgVersion setVersionOK(boolean versionOK) {
    BorgConfig setVersionOK(boolean versionOK) {
        this.versionOK = versionOK;
        return this;
    }
    BorgVersion setVersion(String version) {
        this.version = version;
    BorgConfig setVersion(String version) {
        if (StringUtils.isNotBlank(version)) {
            this.version = version;
        } else {
            this.version = BORG_DEFAULT_DOWNLOAD_VERSION;
        }
        return this;
    }
    BorgVersion setStatusMessage(String statusMessage) {
    BorgConfig setStatusMessage(String statusMessage) {
        this.statusMessage = statusMessage;
        return this;
    }
@@ -125,4 +113,14 @@
        }
        return version.split("\\.");
    }
    @Override
    public String toString() {
        return "BorgConfig{" +
                ", borgBinary='" + borgBinary + '\'' +
                ", versionOK=" + versionOK +
                ", version='" + version + '\'' +
                ", statusMessage='" + statusMessage + '\'' +
                '}';
    }
}
borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
@@ -26,7 +26,7 @@
        return instance;
    }
    private BorgVersion borgVersion = new BorgVersion();
    private BorgConfig borgConfig = new BorgConfig();
    public void initialize() {
        Configuration configuration = ConfigurationHandler.getConfiguration();
@@ -35,10 +35,10 @@
                return;
            }
        }
        borgVersion.setBinariesDownloadVersion(configuration.getBorgVersion());
        borgConfig.setVersion(configuration.getBorgVersion());
        initialize(getBinary(RunningMode.getOSType()));
        if (!borgVersion.isVersionOK()) {
            log.warn("No working borg version found. Please configure a borg version with minimal version '" + borgVersion.getMinimumRequiredBorgVersion() + "'.");
        if (!borgConfig.isVersionOK()) {
            log.warn("No working borg version found. Please configure a borg version with minimal version '" + borgConfig.getMinimumRequiredBorgVersion() + "'.");
        }
    }
@@ -46,13 +46,19 @@
     * Configures a new borg configuration if modifications was done.
     *
     * @param newConfiguration The new configuration with the (new) borg command to use (executable).
     * @param borgBinary       The id of the borg binary (Mac OS X, Linux 64, manual etc.)
     * @param newBorgConfig     The new config, including the id of the borg binary (Mac OS X, Linux 64, manual etc.) and version.
     */
    public void configure(ServerConfiguration newConfiguration, String borgBinary) {
    public void configure(ServerConfiguration newConfiguration, BorgConfig newBorgConfig) {
        ServerConfiguration configuration = ServerConfiguration.get();
        boolean borgBinaryChanged = !StringUtils.equals(borgVersion.getBorgBinary(), borgBinary);
        borgVersion.setBorgBinary(borgBinary); // Update borg binary (if changed).
        boolean manualBorgCommand = "manual".equals(borgBinary);
        String oldBorgBinary = borgConfig.getBorgBinary();
        String oldVersion = borgConfig.getVersion();
        String newBorgBinary = newBorgConfig.getBorgBinary();
        String newVersion = newConfiguration.getBorgVersion();
        boolean borgBinaryChanged = !StringUtils.equals(oldBorgBinary, newBorgBinary) ||
                !StringUtils.equals(oldVersion, newVersion);
        borgConfig.setBorgBinary(newBorgBinary); // Update borg binary (if changed).
        borgConfig.setVersion(newVersion);
        boolean manualBorgCommand = "manual".equals( newBorgConfig.getBorgBinary());
        if (manualBorgCommand) {
            boolean borgCommandChanged = !StringUtils.equals(newConfiguration.getBorgCommand(), configuration.getBorgCommand());
            if (borgCommandChanged) {
@@ -61,9 +67,16 @@
            }
        } else {
            if (borgBinaryChanged) {
                initialize(getBinary(borgBinary));
                initialize(getBinary(newBorgBinary));
            }
            newConfiguration.setBorgCommand(configuration.getBorgCommand()); // borg command of this class overwrites new configuration for mode != 'manual'.
            newConfiguration.setBorgVersion(newVersion);
            if (!StringUtils.equals(oldVersion, newVersion)) {
                log.info("Version '" + oldVersion + "' -> '" + newVersion + "'.");
            }
            if (!StringUtils.equals(oldBorgBinary, newBorgBinary)) {
                log.info("Binary '" + oldBorgBinary + "' -> '" + newBorgBinary + "'.");
            }
        }
    }
@@ -75,7 +88,7 @@
        File file = download(binary);
        if (file != null) {
            configuration.setBorgCommand(file.getAbsolutePath());
            borgVersion.setBorgBinary(binary[0]);
            borgConfig.setBorgBinary(binary[0]);
        }
        return version(configuration);
    }
@@ -83,9 +96,9 @@
    private boolean version(Configuration configuration) {
        String borgCommand = configuration.getBorgCommand();
        if (StringUtils.isNotBlank(borgCommand)) {
            for (String[] borgBinary : borgVersion.getBorgBinaries()) {
            for (String[] borgBinary : borgConfig.getBorgBinaries()) {
                if (borgCommand.contains(borgBinary[0])) {
                    borgVersion.setBorgBinary(borgBinary[0]);
                    borgConfig.setBorgBinary(borgBinary[0]);
                    break;
                }
            }
@@ -94,22 +107,22 @@
        boolean versionOK = false;
        String msg = null;
        if (versionString != null) {
            borgVersion.setVersion(versionString);
            int cmp = BorgVersion.compareVersions(versionString, borgVersion.getMinimumRequiredBorgVersion());
            borgConfig.setVersion(versionString);
            int cmp = BorgConfig.compareVersions(versionString, borgConfig.getMinimumRequiredBorgVersion());
            if (cmp < 0) {
                msg = "Found borg version '" + versionString + "' is less than minimum required version '" + borgVersion.getMinimumRequiredBorgVersion() + "'.";
                msg = "Found borg version '" + versionString + "' is less than minimum required version '" + borgConfig.getMinimumRequiredBorgVersion() + "'.";
                log.info(msg);
            } else {
                versionOK = true;
                msg = "Found borg '" + configuration.getBorgCommand() + "', version: " + versionString + " (equals to or newer than '" + borgVersion.getMinimumRequiredBorgVersion()
                msg = "Found borg '" + configuration.getBorgCommand() + "', version: " + versionString + " (equals to or newer than '" + borgConfig.getMinimumRequiredBorgVersion()
                        + "', OK).";
                log.info(msg);
            }
        } else {
            msg = "Couldn't execute borg command '" + configuration.getBorgCommand() + "'.";
        }
        borgVersion.setVersionOK(versionOK);
        borgVersion.setStatusMessage(msg);
        borgConfig.setVersionOK(versionOK);
        borgConfig.setStatusMessage(msg);
        return versionOK;
    }
@@ -133,7 +146,7 @@
        if (os == null) {
            return null;
        }
        for (String[] binary : borgVersion.getBorgBinaries()) {
        for (String[] binary : borgConfig.getBorgBinaries()) {
            if (binary[0].contains(os)) {
                return binary;
            }
@@ -156,7 +169,7 @@
            // File already downloaded, nothing to do.
            return file;
        }
        String url = borgVersion.getBinariesDownloadUrl() + getDownloadFilename(binary);
        String url = borgConfig.getBinariesDownloadUrl() + getDownloadFilename(binary);
        log.info("Trying to download borg binary '" + binary[0] + "' (" + binary[1] + ") from url: " + url + "...");
        HttpClientBuilder builder = HttpClients.custom()
                .setDefaultRequestConfig(RequestConfig.custom()
@@ -186,7 +199,7 @@
            log.info("Creating binary directory: " + dir.getAbsolutePath());
            dir.mkdirs();
        }
        return new File(dir, getDownloadFilename(binary) + "-" + borgVersion.getBinariesDownloadVersion());
        return new File(dir, getDownloadFilename(binary) + "-" + borgConfig.getVersion());
    }
    private String getDownloadFilename(String[] binary) {
@@ -196,7 +209,7 @@
    private BorgInstallation() {
    }
    public BorgVersion getBorgVersion() {
        return this.borgVersion;
    public BorgConfig getBorgConfig() {
        return this.borgConfig;
    }
}
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/ServerConfiguration.kt
@@ -20,6 +20,10 @@
        webDevelopmentMode = other.webDevelopmentMode
    }
    override fun toString(): String {
        return "${super.toString()}, port=[$port], webDevelopmentMode=[$webDevelopmentMode]"
    }
    companion object {
        val supportedLanguages = arrayOf("en", "de")
        const val WEBSERVER_PORT_DEFAULT = 9042
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/ConfigurationInfo.kt
@@ -1,9 +1,9 @@
package de.micromata.borgbutler.server.rest
import de.micromata.borgbutler.server.BorgVersion
import de.micromata.borgbutler.server.BorgConfig
import de.micromata.borgbutler.server.ServerConfiguration
class ConfigurationInfo(
    var serverConfiguration: ServerConfiguration? = null,
    var borgVersion: BorgVersion? = null
    var borgConfig: BorgConfig? = null
)
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/ConfigurationRest.kt
@@ -2,13 +2,11 @@
import de.micromata.borgbutler.cache.ButlerCache
import de.micromata.borgbutler.config.ConfigurationHandler
import de.micromata.borgbutler.json.JsonUtils
import de.micromata.borgbutler.server.BorgInstallation
import de.micromata.borgbutler.server.ServerConfiguration
import de.micromata.borgbutler.server.user.UserData
import de.micromata.borgbutler.server.user.UserManager
import mu.KotlinLogging
import org.apache.commons.lang3.StringUtils
import org.springframework.web.bind.annotation.*
private val log = KotlinLogging.logger {}
@@ -21,19 +19,19 @@
    fun getConfig(): ConfigurationInfo {
        val configurationInfo = ConfigurationInfo()
        configurationInfo.serverConfiguration = ServerConfiguration.get()
        configurationInfo.borgVersion = BorgInstallation.getInstance().borgVersion
        configurationInfo.borgConfig = BorgInstallation.getInstance().borgConfig
        return configurationInfo
    }
    @PostMapping("config")
    fun setConfig(@RequestBody configurationInfo: ConfigurationInfo) {
        val configurationHandler = ConfigurationHandler.getInstance()
        BorgInstallation.getInstance()
            .configure(configurationInfo.serverConfiguration, configurationInfo.borgVersion?.borgBinary)
        log.info("server-config: ${configurationInfo.serverConfiguration}, borg-binary: ${configurationInfo.borgConfig?.borgBinary}")
        BorgInstallation.getInstance().configure(configurationInfo.serverConfiguration, configurationInfo.borgConfig)
        val configuration: ServerConfiguration = ServerConfiguration.get()
        configurationInfo.serverConfiguration?.let {
            configuration.copyFrom(it)
        }
        val configurationHandler = ConfigurationHandler.getInstance()
        configurationHandler.save()
    }
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/SystemInfo.kt
@@ -1,7 +1,7 @@
package de.micromata.borgbutler.server.rest
import de.micromata.borgbutler.BorgQueueStatistics
import de.micromata.borgbutler.server.BorgVersion
import de.micromata.borgbutler.server.BorgConfig
/**
 * Statistics of all the job queues, especially the number of total queued and running jobs.
@@ -13,7 +13,7 @@
        private set
    var configurationOK = false
        private set
    var borgVersion: BorgVersion? = null
    var borgConfig: BorgConfig? = null
        private set
    fun setQueueStatistics(queueStatistics: BorgQueueStatistics?): SystemInfo {
@@ -26,8 +26,8 @@
        return this
    }
    fun setBorgVersion(borgVersion: BorgVersion?): SystemInfo {
        this.borgVersion = borgVersion
    fun setBorgConfig(borgConfig: BorgConfig?): SystemInfo {
        this.borgConfig = borgConfig
        return this
    }
}
borgbutler-server/src/main/kotlin/de/micromata/borgbutler/server/rest/SystemInfoRest.kt
@@ -16,11 +16,11 @@
     */
    @GetMapping("info")
    fun statistics(): SystemInfo {
        val borgVersion = BorgInstallation.getInstance().borgVersion
        val systemInfonfo = SystemInfo()
        val borgConfig = BorgInstallation.getInstance().borgConfig
        val systemInfo = SystemInfo()
            .setQueueStatistics(BorgQueueExecutor.getInstance().statistics)
            .setConfigurationOK(borgVersion.isVersionOK)
            .setBorgVersion(borgVersion)
        return systemInfonfo
            .setConfigurationOK(borgConfig.isVersionOK)
            .setBorgConfig(borgConfig)
        return systemInfo
    }
}
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgConfigTest.java
New file
@@ -0,0 +1,20 @@
package de.micromata.borgbutler.server;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class BorgConfigTest {
    @Test
    void versionCompareTest() {
        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            BorgConfig.compareVersions(null, "");
        });
        Assertions.assertThrows(IllegalArgumentException.class, () -> {
            BorgConfig.compareVersions("", "");
        });
        Assertions.assertEquals(-1, BorgConfig.compareVersions("1.1.8", "1.1.16"));
        Assertions.assertEquals(0, BorgConfig.compareVersions("1.1.8", "1.1.8"));
        Assertions.assertEquals(1, BorgConfig.compareVersions("1.1.16", "1.1.8"));
    }
}
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
@@ -21,9 +21,24 @@
        borgInstallation.initialize();
    }
    @Test
    void configureTest() {
        ConfigurationHandler.setConfigClazz(ServerConfiguration.class);
        BorgInstallation borgInstallation = BorgInstallation.getInstance();
        borgInstallation.initialize();
        BorgConfig borgConfig = new BorgConfig();
        borgConfig.setVersion("1.1.15");
        borgConfig.setBorgBinary("freebsd64");
        ServerConfiguration serverConfig = ServerConfiguration.get();
        borgInstallation.configure(serverConfig, borgConfig);
        String expected = "freebsd64-1.1.15";
        assertTrue(serverConfig.getBorgCommand().endsWith(expected), "String '" + serverConfig.getBorgCommand() + "' should end with '" + expected + "'.");
    }
    @Test
    void downloadTest() {
        String version = new BorgVersion().getBinariesDownloadVersion();
        String version = new BorgConfig().getVersion();
        checkDownload(RunningMode.OSType.LINUX, "borg-linux64-" + version);
        checkDownload(RunningMode.OSType.MAC_OS, "borg-macosx64-" + version);
        checkDownload(RunningMode.OSType.FREEBSD, "borg-freebsd64-" + version);
borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgVersionTest.java
File was deleted
borgbutler-server/src/test/kotlin/de/micromata/borgbutler/server/user/UserFilterTest.kt
@@ -1,6 +1,5 @@
package de.micromata.borgbutler.server.user
import de.micromata.borgbutler.server.BorgVersion
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
borgbutler-webapp/src/components/views/config/ConfigurationServerTab.jsx
@@ -35,8 +35,8 @@
            .then((data) => {
                this.setState({
                    loading: false,
                    borgVersion: data.borgVersion,
                    borgBinary: data.borgVersion.borgBinary,
                    borgConfig: data.borgConfig,
                    borgBinary: data.borgConfig.borgBinary,
                    ...data.serverConfiguration
                })
            })
@@ -56,6 +56,7 @@
            loading: true,
            failed: false,
            port: 9042,
            borgVersion: "1.1.17",
            webdevelopmentMode: false,
            showDemoRepos: true,
            maxArchiveContentCacheCapacityMb: 100,
@@ -88,9 +89,10 @@
                maxArchiveContentCacheCapacityMb: this.state.maxArchiveContentCacheCapacityMb,
                webDevelopmentMode: this.state.webDevelopmentMode,
                showDemoRepos: this.state.showDemoRepos,
                borgCommand: this.state.borgCommand
                borgCommand: this.state.borgCommand,
                borgVersion: this.state.borgVersion,
            },
            borgVersion: {
            borgConfig: {
                borgBinary: this.state.borgBinary
            }
        };
@@ -121,12 +123,12 @@
        if (this.state.failed) {
            return <ErrorAlertGenericRestFailure handleClick={this.loadConfig}/>;
        }
        const borgVersion = this.state.borgVersion;
        const borgConfig = this.state.borgConfig;
        let borgInfoColor = 'success';
        let borgInfoMessage = `Borg version '${borgVersion.version}' is OK.`;
        if (!borgVersion.versionOK) {
        let borgInfoMessage = `Borg version '${borgConfig.version}' is OK.`;
        if (!borgConfig.versionOK) {
            borgInfoColor = 'danger';
            borgInfoMessage = borgVersion.statusMessage;
            borgInfoMessage = borgConfig.statusMessage;
        }
        return (
            <React.Fragment>
@@ -138,9 +140,9 @@
                                value={this.state.borgBinary}
                                name={'borgBinary'}
                                onChange={this.handleTextChange}
                                hint={`Choose your OS and BorgButler will download and use a ready to run borg binary from ${borgVersion.binariesDownloadUrl} or choose a manual installed version.`}
                                hint={`Choose your OS and BorgButler will download and use a ready to run borg binary from ${borgConfig.binariesDownloadUrl} or choose a manual installed version.`}
                            >
                                {borgVersion.borgBinaries
                                {borgConfig.borgBinaries
                                    .map((binary, index) => <FormOption label={binary[1]} value={binary[0]}
                                                                        key={index}/>)}
                                <FormOption label={'Manual'} value={'manual'}/>
@@ -157,6 +159,10 @@
                            {borgInfoMessage}
                        </Alert>
                    </FormGroup>
                    <FormLabelInputField label={'Borg version'} fieldLength={2}
                                         name={'borgVersion'} value={this.state.borgVersion}
                                         onChange={this.handleTextChange}
                                         placeholder="Enter borg version (e. g. 1.1.17)"/>
                    <FormLabelInputField label={'Port'} fieldLength={2} type="number" min={0} max={65535}
                                         step={1}
                                         name={'port'} value={this.state.port}