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

Kai Reinhard
14.01.2021 8acbe1c3762eea533a15863f840ec452d9e561b9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package org.micromata.borgbutler.config
 
import de.micromata.borgbutler.config.Definitions
import de.micromata.borgbutler.json.JsonUtils
import mu.KotlinLogging
import org.apache.commons.io.FileUtils
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
 
private val log = KotlinLogging.logger {}
 
/**
 * Reads and writes config file borgbutler-config.json/borgbutler-config.yaml
 */
class ConfigurationHandler private constructor(butlerHomeDir: String? = null) {
    val configFile: File
    private val configBackupDir: File
    var workingDir: File
        private set
    private val butlerHomeDir: File? = null
    private var configuration: Configuration? = null
 
    private fun read() {
        if (configFile.canRead()) {
            log.info("Reading config file '" + configFile.absolutePath + "'")
            val jsonConfigFile = File(workingDir, CONFIG_FILENAME)
            if (jsonConfigFile.canRead()) {
                val yaml = FileUtils.readFileToString(jsonConfigFile, Definitions.STD_CHARSET)
                configuration = YamlUtils.fromYaml(configClazz, yaml)
            }
        } else {
            Legacy.readOldJsonConfigFile(workingDir, OLD_JSON_CONFIG_FILENAME)?.let {
                configuration = it
                save()
            }
        }
        try {
            if (configuration == null) {
                try {
                    configuration = configClazz.getDeclaredConstructor().newInstance()
                    save()
                } catch (ex: Exception) {
                    log.error(
                        "Internal error: Can't instantiate object of type '" + configClazz + "': " + ex.message,
                        ex
                    )
                    return
                }
            }
            configuration?.getRepoConfigs()?.filter { it.displayName.isNullOrBlank() }?.forEach { repoConfig ->
                repoConfig.displayName = repoConfig.repo
            }
            configuration?.setWorkingDir(workingDir)
        } catch (ex: IOException) {
            log.error("Error while trying to read from config file: " + configFile.absolutePath + ": " + ex.message, ex)
            return
        }
    }
 
    fun save() {
        configuration?.getRepoConfigs()?.filter { !it.passphrase.isNullOrBlank() }?.forEach { repoConfig ->
            log.info("Removing password command from config because password command is given: " + repoConfig.passwordCommand)
            repoConfig.passphrase = null // Don't use password (anymore) if password command is available.
        }
        val yaml = YamlUtils.toYaml(configuration)
        try {
            if (configFile.exists()) {
                // Create backup-file first:
                makeBackup(configFile)
            }
            log.info("Writing config file '" + configFile.absolutePath + "'")
            FileUtils.write(configFile, yaml, Definitions.STD_CHARSET)
        } catch (ex: IOException) {
            log.error("Error while trying to write config file: " + configFile.absolutePath + ": " + ex.message, ex)
        }
    }
 
    private fun makeBackup(file: File) {
        val formatter = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss'-'")
        val backupFile = File(configBackupDir, formatter.format(Date()) + file.name)
        log.info("Creating backup file first: '" + backupFile.absolutePath + "'")
        FileUtils.copyFile(file, backupFile)
    }
 
    companion object {
        private var instance: ConfigurationHandler? = null
        private const val BUTLER_HOME_DIR = ".borgbutler"
        private const val OLD_JSON_CONFIG_FILENAME = "borgbutler-config.json"
        private const val CONFIG_FILENAME = "borgbutler.config"
        private const val CONFIG_BACKUP_DIR = "backup"
        private var configClazz: Class<out Configuration> = Configuration::class.java
 
        @kotlin.jvm.JvmStatic
        fun init(butlerHomeDir: String?) {
            if (instance != null) {
                throw RuntimeException("ConfigurationHandler already initialized")
            }
            instance = ConfigurationHandler(butlerHomeDir)
        }
 
        @kotlin.jvm.JvmStatic
        fun getInstance(): ConfigurationHandler? {
            if (instance == null) instance = ConfigurationHandler()
            return instance
        }
 
        @kotlin.jvm.JvmStatic
        fun getConfiguration(): Configuration? {
            return getInstance()!!.configuration
        }
 
        @kotlin.jvm.JvmStatic
        fun setConfigClazz(configClazz: Class<out Configuration>) {
            Companion.configClazz = configClazz
        }
 
        @kotlin.jvm.JvmStatic
        fun getConfigClazz(): Class<out Configuration> {
            return configClazz
        }
    }
 
    init {
        workingDir = if (butlerHomeDir != null) {
            File(butlerHomeDir)
        } else {
            File(System.getProperty("user.home"), BUTLER_HOME_DIR)
        }
        log.info("Using directory '" + workingDir.getAbsolutePath() + "' as BorgButler's home directory.")
        if (!workingDir.exists()) {
            log.info("Creating borg-butlers working directory: " + workingDir.getAbsolutePath())
            workingDir.mkdirs()
        }
        configFile = File(workingDir, CONFIG_FILENAME)
        configBackupDir = File(workingDir, CONFIG_BACKUP_DIR)
        if (!configBackupDir.exists()) {
            log.info("Creating borg-butlers backup directory: " + configBackupDir.absolutePath)
            configBackupDir.mkdirs()
        }
        read()
    }
}