/* Lists all the backup files, regardless of their location. Backups are returned in json format. The request must be a GET. Form values: "fmt", "filename" is the only supported value at present. Defaults to "filename" if absent or if left blank "dbname": the name of the database for which to query backups of. If left blank, returns the backups for all databases.. "globals": set to the string "true" if files with the suffix ".globals" should appear in the output.*/ func BackupListAllHandler(w http.ResponseWriter, request *http.Request) { printFormat := "filename" if request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } showGlobals := false if request.FormValue("globals") == "true" { showGlobals = true } // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") backupList, err := backup.FullListing(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("An error occurred when querying backups")) } switch printFormat { case "filename": w.Header().Set("Content-Type", "application/json") w.Write([]byte(formatFilename(backupList))) //case "timestamp": TODO default: log.Debug(fmt.Sprintf(`api.BackupListHandler() Requested unsupported format.`)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unsupported Format Requested")) } }
/* Should contain a form value dbname which equals the database name e.g. curl www.hostname.com/backup/now -X POST -d "dbname=nameofdatabase" The {how} should be either "now" or "enqueue" */ func BackupHandler(w http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) dbname := request.FormValue("dbname") t := tasks.NewTask() t.Action = "BackupDatabase" t.Data = dbname t.Node = globals.MyIP t.Role = globals.ServiceRole t.TTL = 3600 t.ClusterService = globals.ClusterService t.NodeType = "read" if rdpgconsul.IsWriteNode(globals.MyIP) { t.NodeType = "write" } var err error if dbname != "rdpg" { //Using FindByDatabase to determine if the database actually exists to be backed up. inst, err := instances.FindByDatabase(dbname) if err != nil { log.Error(fmt.Sprintf("admin.BackupHandler() instances.FindByDatabase(%s) Error occurred when searching for database.", dbname)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Error encountered while searching for database")) return } if inst == nil { //...then the database doesn't exist on this cluster. log.Debug(fmt.Sprintf("admin.BackupHandler() Attempt to initiate backup on non-existant database with name: %s", dbname)) w.WriteHeader(http.StatusNotFound) w.Write([]byte("Database not found")) return } } switch vars[`how`] { //Immediately calls Backup() and performs the backup case "now": err = t.BackupDatabase() if err != nil { log.Error(fmt.Sprintf(`api.BackupHandler() Task.BackupDatabase() %+v ! %s`, t, err)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Error encountered while trying to perform backup")) return } w.Write([]byte("Backup completed.")) case "enqueue": // Queues up a backup to be done with a worker thread gets around to it. // This call returns after the queuing process is done; not after the backup is done. err = t.Enqueue() if err != nil { log.Error(fmt.Sprintf(`api.BackupHandler() Task.Enqueue() %+v ! %s`, t, err)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Error while trying to queue")) return } w.Write([]byte("Backup successfully queued.")) default: w.WriteHeader(http.StatusNotFound) } }
/* Lists all the backup files at the desired location. The {where} should be "local" or "remote". The "local" option finds all the backup files on the local filesystem. The "remote" option will display all of the backup files on the remote storage, such as S3, but this feature is not yet implemented. Backups are returned in json format. The request must be a POST. Form values: "fmt", "filename" is the only supported value at present. Defaults to "filename" if absent or if left blank "dbname": the name of the database for which to query backups of. Will eventually allow being left blank to return all database backups, but not yet. */ func BackupListHandler(w http.ResponseWriter, request *http.Request) { //Default printing format to print pretty timestamps. So pretty. printFormat := "filename" if request.Method == "POST" && request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } backupList := []databaseBackupList{} // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") // Where are we getting the files from? vars := mux.Vars(request) var err error switch vars["where"] { case "local": backupList, err = handleLocalListing(dbname) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } case "remote": log.Warn(fmt.Sprintf(`api.BackupListHandler() Remote Backup Listing not yet supported.`)) w.WriteHeader(http.StatusNotImplemented) w.Write([]byte("Not Yet Implemented")) return } switch printFormat { case "filename": outputString := "{ " var separator string for i, d := range backupList { if i == 0 { separator = "" } else { separator = ", " } outputString = outputString + fmt.Sprintf("%s\"%s\": [", separator, d.Database) for j, v := range d.Backups { if j == 0 { separator = "" } else { separator = ", " } outputString = outputString + fmt.Sprintf("%s{ \"Name\": \"%s\", \"Bytes\": \"%s\" }", separator, v.Name, v.Size) } outputString = outputString + "]" } outputString = outputString + "}" w.Write([]byte(outputString)) //case "timestamp": TODO default: log.Debug(fmt.Sprintf(`api.BackupListHandler() Requested unsupported format.`)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unsupported Format Requested")) } }
func writeJSONResponse(writer http.ResponseWriter, status int, description string) { resp := Response{Status: status, Description: description} msg, err := json.Marshal(resp) if err != nil { log.Error(err.Error()) } log.Debug("Returning response: " + string(msg)) writer.WriteHeader(status) http.Error(writer, string(msg), status) }
/* Lists all the backup files at the desired location. The {where} should be "local" or "remote". The "local" option finds all the backup files on the local filesystem. The "remote" option will display all of the backup files on the remote storage, such as S3. Backups are returned in json format. The request must be a GET. Form values: "fmt", "filename" is the only supported value at present. Defaults to "filename" if absent or if left blank "dbname": the name of the database for which to query backups of. If left blank, returns the backups for all databases. "globals": set to the string "true" if files with the suffix ".globals" should appear in the output.*/ func BackupListHandler(w http.ResponseWriter, request *http.Request) { printFormat := "filename" if request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } showGlobals := false if request.FormValue("globals") == "true" { showGlobals = true } backupList := []backup.DatabaseBackupList{} // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") // Where are we getting the files from? vars := mux.Vars(request) var err error switch vars["where"] { case "local": backupList, err = backup.LocalListing(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } case "remote": rdpgs3.ReinitializeS3Credentials() if !rdpgs3.Configured { w.WriteHeader(http.StatusGone) w.Write([]byte("Remote storage has not been configured")) } backupList, err = backup.RemoteListing(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } default: w.WriteHeader(http.StatusNotFound) return } switch printFormat { case "filename": w.Header().Set("Content-Type", "application/json") w.Write([]byte(formatFilename(backupList))) //case "timestamp": TODO default: log.Debug(fmt.Sprintf(`api.BackupListHandler() Requested unsupported format.`)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unsupported Format Requested")) } }
func BackupDiffHandler(w http.ResponseWriter, request *http.Request) { //Default printing format to print pretty timestamps. So pretty. printFormat := "filename" if request.Method == "POST" && request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } showGlobals := false if request.FormValue("globals") == "true" { showGlobals = true } // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") // Where are we getting the files from? vars := mux.Vars(request) //Do we actually have s3? rdpgs3.ReinitializeS3Credentials() if !rdpgs3.Configured { w.WriteHeader(http.StatusGone) w.Write([]byte("Remote storage has not been configured")) } var localDiff, remoteDiff []backup.DatabaseBackupList var err error //The "both" path uses a different function if vars["where"] != "both" { localDiff, remoteDiff, err = backup.Diff(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } } //====Begin inner function formatter := func(localList []backup.DatabaseBackupList, remoteList []backup.DatabaseBackupList, printFormat string) (outputString string, err error) { if localList == nil && remoteList == nil { errorMessage := fmt.Sprintf("api.BackupDiffHandler ! Can't handle formatting without input") log.Error(errorMessage) return "", errors.New(errorMessage) } switch printFormat { case "filename": if localList == nil { // only/remote outputString = formatFilename(remoteList) } else if remoteList == nil { //only/local outputString = formatFilename(localList) } else { //only/both outputString = `{ "local": ` + formatFilename(localList) + `, "remote": ` + formatFilename(remoteList) + ` }` } default: log.Debug(fmt.Sprintf(`api.BackupDiffHandler() Requested unsupported format.`)) return "", errors.New(fmt.Sprintf("Unsupported Printing Format: %s. Valid format is 'filename'", printFormat)) } return } //====End inner function var output string switch vars["where"] { case "local": output, err = formatter(localDiff, nil, printFormat) case "remote": output, err = formatter(nil, remoteDiff, printFormat) case "diff": output, err = formatter(localDiff, remoteDiff, printFormat) case "both": bothList, err := backup.Both(dbname, false) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } output, err = formatter(bothList, nil, printFormat) default: w.WriteHeader(http.StatusNotFound) } if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } w.Header().Set("Content-Type", "application/json") w.Write([]byte(output)) }