feat(database): initial commit

feat2901-introduce-backup
ssbkang 2019-07-22 23:33:47 +12:00
parent cc487ae68a
commit 3e0ec6a379
8 changed files with 136 additions and 0 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/cron"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/database"
"github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/api/exec"
"github.com/portainer/portainer/api/filesystem"
@ -100,6 +101,10 @@ func initLDAPService() portainer.LDAPService {
return &ldap.Service{}
}
func initDatabaseService() portainer.DatabaseService {
return &database.Service{}
}
func initGitService() portainer.GitService {
return &git.Service{}
}
@ -526,6 +531,8 @@ func main() {
gitService := initGitService()
databaseService := initDatabaseService()
cryptoService := initCryptoService()
digitalSignatureService := initDigitalSignatureService()
@ -684,6 +691,7 @@ func main() {
ComposeStackManager: composeStackManager,
ExtensionManager: extensionManager,
CryptoService: cryptoService,
DatabaseService: databaseService,
JWTService: jwtService,
FileService: fileService,
LDAPService: ldapService,

44
api/database/database.go Normal file
View File

@ -0,0 +1,44 @@
package database
import (
"github.com/boltdb/bolt"
//"strconv"
//"net/http"
//"io/ioutil"
)
const (
databaseFileName = "portainer.db"
)
// Service represents a service for managing Database.
type Service struct{}
// DatabaseExport makes the BoltDB into read only mode, takes a backup and then put it back to writable mode.
func (service *Service) DatabaseExport(storePath string) (int64, error) {
//var databaseExport int64
//var w http.ResponseWriter
// var r io.Reader
dataStorePath := storePath + "/" + databaseFileName
_, err := bolt.Open(dataStorePath, 0666, &bolt.Options{ReadOnly: true})
/*
db, err := bolt.Open(dataStorePath, 0666, &bolt.Options{ReadOnly: true})
if err != nil {
return 0, err
}
err := db.View(func(tx *bolt.Tx) error {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="portainer.db"`)
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
databaseExport, err := tx.WriteTo(w)
return nil
})
if err != nil {
return 0, err
}
*/
return 0, err
}

View File

@ -84,6 +84,11 @@ func (service *Service) GetBinaryFolder() string {
return path.Join(service.fileStorePath, BinaryStorePath)
}
// GetRootFolder returns the full path to the root store on the filesystem
func (service *Service) GetRootFolder() string {
return service.fileStorePath
}
// ExtractExtensionArchive extracts the content of an extension archive
// specified as raw data into the binary store on the filesystem
func (service *Service) ExtractExtensionArchive(data []byte) error {

View File

@ -0,0 +1,29 @@
package database
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
//"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api"
)
func (handler *Handler) databaseExport(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
storePath := handler.FileService.GetRootFolder()
databaseExport, err := handler.DatabaseService.DatabaseExport(storePath)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Error exporting database", err}
}
database := &portainer.Database{
DatabaseExport: databaseExport,
}
//w.Header().Set("Content-Type", "application/octet-stream")
//w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
//w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
return response.JSON(w, database)
}

View File

@ -0,0 +1,28 @@
package database
import (
"net/http"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/security"
)
// Handler is the HTTP handler used to handle webhook operations.
type Handler struct {
*mux.Router
DatabaseService portainer.DatabaseService
FileService portainer.FileService
}
// NewHandler creates a handler to manage settings operations.
func NewHandler(bouncer *security.RequestBouncer) *Handler {
h := &Handler{
Router: mux.NewRouter(),
}
h.Handle("/database",
//bouncer.RestrictedAccess(httperror.LoggerHandler(h.databaseExport))).Methods(http.MethodGet)
bouncer.PublicAccess(httperror.LoggerHandler(h.databaseExport))).Methods(http.MethodGet)
return h
}

View File

@ -9,6 +9,7 @@ import (
"github.com/portainer/portainer/api/http/handler/roles"
"github.com/portainer/portainer/api/http/handler/auth"
"github.com/portainer/portainer/api/http/handler/database"
"github.com/portainer/portainer/api/http/handler/dockerhub"
"github.com/portainer/portainer/api/http/handler/endpointgroups"
"github.com/portainer/portainer/api/http/handler/endpointproxy"
@ -34,6 +35,7 @@ import (
// Handler is a collection of all the service handlers.
type Handler struct {
AuthHandler *auth.Handler
DatabaseHandler *database.Handler
DockerHubHandler *dockerhub.Handler
EndpointGroupHandler *endpointgroups.Handler
EndpointHandler *endpoints.Handler
@ -63,6 +65,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasPrefix(r.URL.Path, "/api/auth"):
http.StripPrefix("/api", h.AuthHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/database"):
http.StripPrefix("/api", h.DatabaseHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/dockerhub"):
http.StripPrefix("/api", h.DockerHubHandler).ServeHTTP(w, r)
case strings.HasPrefix(r.URL.Path, "/api/endpoint_groups"):

View File

@ -9,6 +9,7 @@ import (
"github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/api/http/handler"
"github.com/portainer/portainer/api/http/handler/auth"
"github.com/portainer/portainer/api/http/handler/database"
"github.com/portainer/portainer/api/http/handler/dockerhub"
"github.com/portainer/portainer/api/http/handler/endpointgroups"
"github.com/portainer/portainer/api/http/handler/endpointproxy"
@ -51,6 +52,7 @@ type Server struct {
JobScheduler portainer.JobScheduler
Snapshotter portainer.Snapshotter
RoleService portainer.RoleService
DatabaseService portainer.DatabaseService
DockerHubService portainer.DockerHubService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
@ -122,6 +124,10 @@ func (server *Server) Start() error {
var roleHandler = roles.NewHandler(requestBouncer)
roleHandler.RoleService = server.RoleService
var databaseHandler = database.NewHandler(requestBouncer)
databaseHandler.DatabaseService = server.DatabaseService
databaseHandler.FileService = server.FileService
var dockerHubHandler = dockerhub.NewHandler(requestBouncer)
dockerHubHandler.DockerHubService = server.DockerHubService
@ -225,6 +231,7 @@ func (server *Server) Start() error {
server.Handler = &handler.Handler{
RoleHandler: roleHandler,
AuthHandler: authHandler,
DatabaseHandler: databaseHandler,
DockerHubHandler: dockerHubHandler,
EndpointGroupHandler: endpointGroupHandler,
EndpointHandler: endpointHandler,

View File

@ -224,6 +224,11 @@ type (
Password string `json:"Password,omitempty"`
}
// Database represents all the required information to connect and use the database features
Database struct {
DatabaseExport int64 `json:"DatabaseExport"`
}
// EndpointID represents an endpoint identifier
EndpointID int
@ -575,6 +580,11 @@ type (
Valid bool `json:"Valid,omitempty"`
}
// Server defines the interface to serve the API
DatabaseService interface {
DatabaseExport(storePath string) (int64, error)
}
// CLIService represents a service for managing CLI
CLIService interface {
ParseFlags(version string) (*CLIFlags, error)
@ -789,6 +799,7 @@ type (
GetScheduleFolder(identifier string) string
ExtractExtensionArchive(data []byte) error
GetBinaryFolder() string
GetRootFolder() string
}
// GitService represents a service for managing Git