diff --git a/Compressor/Compression.go b/Compressor/Compression.go index 54d0d3f..eba1707 100644 --- a/Compressor/Compression.go +++ b/Compressor/Compression.go @@ -39,7 +39,7 @@ func compress(fileToWrite *os.File, folderPath string, backupName string){ zr, _ := gzip.NewWriterLevel(fileToWrite, flate.BestCompression) tw := tar.NewWriter(zr) - go fmt.Printf("[%s] Start compression...\n", filepath.Base(folderPath)) + go fmt.Printf("[%s] Start compression...\n", backupName) SQL.NewLogEntry(SQL.GetSQLInstance(), uuid.New(), SQL.LogInfo, backupName, SQL.SQLStage_Compress, SQL.REMOTE_NONE, "Start compression", time.Now()) filepath.Walk(folderPath, func(file string, fi os.FileInfo, err error) error { header, err := tar.FileInfoHeader(fi, file) @@ -60,7 +60,7 @@ func compress(fileToWrite *os.File, folderPath string, backupName string){ logger.Fatal(err) } - go fmt.Printf("[%s] Compressing: %s (%d bytes)\n", filepath.Base(folderPath) ,relPath, fi.Size()) + go fmt.Printf("[%s] Compressing: %s (%d bytes)\n", backupName, relPath, fi.Size()) if _, err := io.Copy(tw, data); err != nil { logger.Fatal(err) } @@ -78,6 +78,6 @@ func compress(fileToWrite *os.File, folderPath string, backupName string){ } - go fmt.Printf("[%s] Compression Done.\n", filepath.Base(folderPath)) + go fmt.Printf("[%s] Compression Done.\n", backupName) SQL.NewLogEntry(SQL.GetSQLInstance(), uuid.New(), SQL.LogInfo, backupName, SQL.SQLStage_Compress, SQL.REMOTE_NONE, "Compression complete.", time.Now()) } \ No newline at end of file diff --git a/README.md b/README.md index 9f64c57..55b0b6b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Trello board: [Click me!](https://trello.com/b/6zWLE6Jm) - Log the Backup progress to a database - Upload the files to a remote storage of your choice (see [Storage Types](#storage-types)) +## Planned features for the Future! +- Backup restore +- Service for scheduled updates +- (Maybe) a web interface ## Database Types - MariaDB @@ -21,9 +25,9 @@ Trello board: [Click me!](https://trello.com/b/6zWLE6Jm) ## Storage types -- Local storage (soon) -- Azure Blob Storage (planned) +- Local storage - Azure File Share +- Azure Blob Storage (planned) - S3 Bucket (far future) - Dropbox (far future) - OneDrive (far future) @@ -32,24 +36,26 @@ Trello board: [Click me!](https://trello.com/b/6zWLE6Jm) | Storage Type | Config Type | |-------------------------|--------------------------| | Azure File Share | azure-fileshare | +| Local Storage | none | ## Config Explaination ### config.json -| Field | Type | Description | -|---------------------|:----------------:|------------------------------------------------| -| localBackupPath | string | Path where local backups are stored | -| **sqlConfig** | ---------------- | ---------------------------------------------- | -| enableSQL | boolean | Enable/Disables the SQL entries -| sqlType | string | See [DatabaseTypes](#database-types) | -| sql-address | string | Address to the SQL Server | -| sql-port | uint16 | SQL Server Port | -| database | string | Database name | -| db-user | string | SQL username from user which should be used | -| db-password | string | SQL password from user which should be used | -| **foldersToBackup** | ---------------- | ---------------------------------------------- | -| backupName | string | .bak file name | -| folderPath | string | Path to folder which should be backed up | -| storageType | string | See [StorageTypes](#storage-types) | -| createLocalBackup | boolean | Sets if .bak file should also be saved locally | \ No newline at end of file +| Field | Type | Description | +|---------------------------|:----------------:|------------------------------------------------| +| localBackupPath | string | Path where local backups are stored | +| **sqlConfig** | ---------------- | ---------------------------------------------- | +| enableSQL | boolean | Enable/Disables the SQL entries +| sqlType | string | See [DatabaseTypes](#database-types) | +| sql-address | string | Address to the SQL Server | +| sql-port | uint16 | SQL Server Port | +| database | string | Database name | +| db-user | string | SQL username from user which should be used | +| db-password | string | SQL password from user which should be used | +| **foldersToBackup** | ---------------- | ---------------------------------------------- | +| backupName | string | .bak file name | +| folderPath | string | Path to folder which should be backed up | +| remoteStorageType | string | See [StorageTypes](#storage-types) | +| targetPath | string | Sets the targetPath for local backups | +| createLocalBackup | boolean | Sets if .bak file should also be saved locally | \ No newline at end of file diff --git a/SQL/MariaDBConnector.go b/SQL/MariaDBConnector.go index ccd3ad3..864e338 100644 --- a/SQL/MariaDBConnector.go +++ b/SQL/MariaDBConnector.go @@ -4,6 +4,7 @@ import ( "database/sql" _ "github.com/go-sql-driver/mysql" "github.com/google/uuid" + "os" "scabiosa/Logging" "scabiosa/Tools" "strconv" @@ -30,7 +31,6 @@ func GetMariaDBInstance(config Tools.Config) MariaDBConnector { return mariadb } - func checkIfEventLogTableExist(db *sql.DB, mariadb MariaDBConnector) bool { rows, _ := db.Query("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'EventLog';", mariadb.Database) if !rows.Next(){ return false } @@ -43,8 +43,8 @@ func checkIfBackupTableExist(db *sql.DB, mariadb MariaDBConnector) bool { return true } -func checkIfBackupEntryExist(db *sql.DB, mariadb MariaDBConnector, backupName string) bool { - rows, _ := db.Query("SELECT * FROM `" + mariadb.Database + "`.Backups WHERE BackupName = '" + backupName + "';") +func checkIfBackupEntryExist(db *sql.DB, mariadb MariaDBConnector, backupName string, hostname string) bool { + rows, _ := db.Query("SELECT * FROM `" + mariadb.Database + "`.Backups WHERE Hostname = ? AND BackupName = ?;", hostname, backupName) if !rows.Next(){ return false; } return true } @@ -61,9 +61,8 @@ func createMariaDBConnection(mariadb MariaDBConnector) *sql.DB{ func (mariadb MariaDBConnector) createDefaultTables(){ logger := Logging.DetailedLogger("MariaDB", "createDefaultTables") - eventLogSQL := "create table " + mariadb.Database +".EventLog\n(\n UUID text null,\n LogType enum ('INFO', 'WARNING', 'ERROR', 'FATAL') null,\n BackupName varchar(256) null,\n Stage enum ('COMPRESS', 'UPLOAD', 'DELETE TMP') null,\n RemoteStorage enum ('AZURE-FILE', 'AZURE-BLOB', 'NONE') null,\n Description text null,\n Timestamp datetime null\n);" - backupSQL := "create table " + mariadb.Database +".Backups\n(\n UUID text null,\n BackupName varchar(256) null,\n LastBackup datetime null,\n LocalBackup tinyint(1) null,\n FilePath varchar(256) null,\n RemoteStorage enum ('AZURE-FILE', 'AZURE-BLOB', 'NONE') null,\n RemotePath varchar(256) null\n);\n\n" - + eventLogSQL := "create table `" + mariadb.Database +"`.EventLog(UUID text null, LogType enum ('INFO', 'WARNING', 'ERROR', 'FATAL') null, Hostname varchar(256) null,BackupName varchar(256) null, Stage enum ('COMPRESS', 'UPLOAD', 'DELETE TMP') null, RemoteStorage enum ('AZURE-FILE', 'AZURE-BLOB', 'NONE') null, Description text null, Timestamp datetime null);" + backupSQL := "create table `" + mariadb.Database +"`.Backups(UUID text null, Hostname varchar(256) null, BackupName varchar(256) null, LastBackup datetime null, LocalBackup tinyint(1) null, FilePath varchar(256) null, RemoteStorage enum ('AZURE-FILE', 'AZURE-BLOB', 'NONE') null, RemotePath varchar(256) null, LocalPath varchar(256) null);" db := createMariaDBConnection(mariadb) @@ -88,26 +87,28 @@ func (mariadb MariaDBConnector) newLogEntry(uuid uuid.UUID, logType LogType, bac logger := Logging.DetailedLogger("MariaDB", "newLogEntry") db := createMariaDBConnection(mariadb) - _, err := db.Query("INSERT INTO `" + mariadb.Database + "`.EventLog VALUES (?, ?, ?, ?, ?, ?, ?);", uuid.String(), strconv.FormatInt(int64(logType), 10), backupName, stage, strconv.FormatInt(int64(storageType), 10), description ,timestamp) + hostname, _ := os.Hostname() + + _, err := db.Query("INSERT INTO `" + mariadb.Database + "`.EventLog VALUES (?, ?, ?, ?, ?, ?, ?, ?);", uuid.String(), strconv.FormatInt(int64(logType), 10), hostname, backupName, stage, strconv.FormatInt(int64(storageType), 10), description ,timestamp) if err != nil{ logger.Fatal(err) } } - -func (mariadb MariaDBConnector) newBackupEntry(backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string){ +func (mariadb MariaDBConnector) newBackupEntry(backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string, localPath string){ logger := Logging.DetailedLogger("MariaDB", "newBackupEntry") - db := createMariaDBConnection(mariadb) - if checkIfBackupEntryExist(db, mariadb, backupName){ - _, err := db.Query("UPDATE `" + mariadb.Database + "`.Backups SET LastBackup = ? WHERE BackupName = ?;", lastBackup, backupName) + hostname, _ := os.Hostname() + + if checkIfBackupEntryExist(db, mariadb, backupName, hostname){ + _, err := db.Query("UPDATE `" + mariadb.Database + "`.Backups SET LastBackup = ?, LocalBackup = ?, RemoteStorage = ?, RemotePath = ?, LocalPath = ? WHERE Hostname = ? AND BackupName = ?;",lastBackup, localBackup, strconv.FormatInt(int64(storageType), 10), remotePath, localPath, hostname, backupName) if err != nil { logger.Fatal(err) } } else { - _, err := db.Query("INSERT INTO `" + mariadb.Database + "`.Backups VALUES (?, ?, ?, ?, ?, ?, ?);", uuid.New(), backupName, lastBackup, localBackup, filePath, strconv.FormatInt(int64(storageType), 10), remotePath) + _, err := db.Query("INSERT INTO `" + mariadb.Database + "`.Backups VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", uuid.New(), hostname, backupName, lastBackup, localBackup, filePath, strconv.FormatInt(int64(storageType), 10), remotePath, localPath) if err != nil { logger.Fatal(err) } diff --git a/SQL/SQLInterface.go b/SQL/SQLInterface.go index a80ce67..8137b5c 100644 --- a/SQL/SQLInterface.go +++ b/SQL/SQLInterface.go @@ -9,7 +9,7 @@ import ( type SQLService interface { createDefaultTables() newLogEntry(uuid uuid.UUID, logType LogType, backupName string, stage SQLStage, storageType RemoteStorageType, description string, timestamp time.Time) - newBackupEntry(backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string) + newBackupEntry(backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string, localPath string) } func CreateDefaultTables(sqlService SQLService){ @@ -26,10 +26,10 @@ func NewLogEntry(sqlService SQLService, uuid uuid.UUID, logType LogType, backupN } } -func NewBackupEntry(sqlService SQLService, backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string){ +func NewBackupEntry(sqlService SQLService, backupName string, lastBackup time.Time, localBackup bool, filePath string, storageType RemoteStorageType, remotePath string, localPath string){ config := Tools.GetConfig() if config.SQLConfig.EnableSQL{ - sqlService.newBackupEntry(backupName, lastBackup, localBackup, filePath, storageType, remotePath) + sqlService.newBackupEntry(backupName, lastBackup, localBackup, filePath, storageType, remotePath, localPath) } } diff --git a/Tools/Config.go b/Tools/Config.go index 49642d2..87feb19 100644 --- a/Tools/Config.go +++ b/Tools/Config.go @@ -21,8 +21,9 @@ type Config struct { BackupName string `json:"backupName"` FolderPath string `json:"folderPath"` RemoteStorageType string `json:"remoteStorageType"` - TargetPath string `json:"targetPath"` - CreateLocalBackup bool `json:"createLocalBackup"` + RemoteTargetPath string `json:"remoteTargetPath"` + CreateLocalBackup bool `json:"createLocalBackup"` + LocalTargetPath string `json:"LocalTargetPath"` } `json:"foldersToBackup"` } @@ -36,9 +37,40 @@ func readConfig() []byte { return file } +func CheckIfConfigExists(){ + logger := Logging.DetailedLogger("ConfigHandler", "CheckIfConfigExists") + + if _, err := os.Stat("config/config.json"); os.IsNotExist(err){ + _, fileErr := os.OpenFile("config/config.json", os.O_CREATE, 0775) + if fileErr != nil{ + logger.Fatal(fileErr) + } + generateDefaultConfig() + } +} + +func generateDefaultConfig() { + logger := Logging.DetailedLogger("ConfigHandler", "GenerateDefaultConfig") + + var config Config + var conf []byte + + conf, err := json.MarshalIndent(config, "", "\t") + //conf, err := json.Marshal(config) + if err != nil { + logger.Fatal(err) + } + + err = os.WriteFile("config/config.json", conf, 0755) + if err != nil { + logger.Fatal(err) + } + +} func GetConfig() Config { - logger := Logging.DetailedLogger("ConfigHandler", "GetConfig()") + + logger := Logging.DetailedLogger("ConfigHandler", "GetConfig") var config Config err := json.Unmarshal(readConfig(), &config) diff --git a/config/config.json b/config/config.json index 21652af..6cbb73d 100644 --- a/config/config.json +++ b/config/config.json @@ -12,9 +12,10 @@ { "backupName": "", "folderPath": "", - "storageType": "", - "targetPath": "", - "createLocalBackup": false + "remoteStorageType": "", + "remoteTargetPath": "", + "createLocalBackup": false, + "localTargetPath": "" } ] } \ No newline at end of file diff --git a/main.go b/main.go index 0d7157e..4e15540 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( ) func main(){ + Tools.CheckIfConfigExists() config := Tools.GetConfig() SQL.CreateDefaultTables(SQL.GetSQLInstance()) @@ -23,22 +24,27 @@ func main(){ if backupItem.RemoteStorageType != "none"{ storage = StorageTypes.CheckStorageType(backupItem.RemoteStorageType) - destPath = checkTmpPath(backupItem.CreateLocalBackup, backupItem.TargetPath) + destPath = checkTmpPath(backupItem.CreateLocalBackup, backupItem.LocalTargetPath) } else { - destPath = backupItem.TargetPath + destPath = backupItem.LocalTargetPath } bakFile := Compressor.CreateBakFile(backupItem.BackupName + getTimeSuffix(), backupItem.FolderPath, destPath, backupItem.BackupName) if backupItem.RemoteStorageType != "none"{ - StorageTypes.UploadFile(storage, bakFile, backupItem.BackupName, backupItem.TargetPath) + StorageTypes.UploadFile(storage, bakFile, backupItem.BackupName, backupItem.RemoteTargetPath) } if !backupItem.CreateLocalBackup && backupItem.RemoteStorageType != "none"{ _ = os.Remove(bakFile) - SQL.NewLogEntry(SQL.GetSQLInstance(), uuid.New(), SQL.LogInfo, backupItem.BackupName, SQL.SQLStage_DeleteTmp, SQL.REMOTE_NONE, "Deleted tmp file" ,time.Now()) - } - SQL.NewBackupEntry(SQL.GetSQLInstance(), backupItem.BackupName, time.Now(), backupItem.CreateLocalBackup, backupItem.FolderPath, StorageTypes.CheckRemoteStorageType(backupItem.RemoteStorageType), StorageTypes.GetAzureStorage().TargetDirectory) + SQL.NewLogEntry(SQL.GetSQLInstance(), uuid.New(), SQL.LogInfo, backupItem.BackupName, SQL.SQLStage_DeleteTmp, SQL.REMOTE_NONE, "Deleted tmp file" ,time.Now()) + } + + if backupItem.RemoteStorageType == "none" { + backupItem.CreateLocalBackup = true + backupItem.RemoteTargetPath = "NONE" + } + SQL.NewBackupEntry(SQL.GetSQLInstance(), backupItem.BackupName, time.Now(), backupItem.CreateLocalBackup, backupItem.FolderPath, StorageTypes.CheckRemoteStorageType(backupItem.RemoteStorageType), backupItem.RemoteTargetPath, backupItem.LocalTargetPath) } }