add support for inter-node communications

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2022-09-25 19:48:55 +02:00
parent a538255034
commit 76e89d07d4
25 changed files with 847 additions and 59 deletions

View File

@@ -187,6 +187,7 @@ var (
sqlTableEventsRules string
sqlTableRulesActionsMapping string
sqlTableTasks string
sqlTableNodes string
sqlTableSchemaVersion string
argon2Params *argon2id.Params
lastLoginMinDelay = 10 * time.Minute
@@ -216,6 +217,7 @@ func initSQLTables() {
sqlTableEventsRules = "events_rules"
sqlTableRulesActionsMapping = "rules_actions_mapping"
sqlTableTasks = "tasks"
sqlTableNodes = "nodes"
sqlTableSchemaVersion = "schema_version"
}
@@ -311,7 +313,7 @@ type ProviderStatus struct {
Error string `json:"error"`
}
// Config provider configuration
// Config defines the provider configuration
type Config struct {
// Driver name, must be one of the SupportedProviders
Driver string `json:"driver" mapstructure:"driver"`
@@ -460,6 +462,9 @@ type Config struct {
// For shared data providers, active transfers are persisted in the database and thus
// quota checks between ongoing transfers will work cross multiple instances
IsShared int `json:"is_shared" mapstructure:"is_shared"`
// Node defines the configuration for this cluster node.
// Ignored if the provider is not shared/shareable
Node NodeConfig `json:"node" mapstructure:"node"`
// Path to the backup directory. This can be an absolute path or a path relative to the config dir
BackupsPath string `json:"backups_path" mapstructure:"backups_path"`
}
@@ -778,6 +783,11 @@ type Provider interface {
updateTaskTimestamp(name string) error
setFirstDownloadTimestamp(username string) error
setFirstUploadTimestamp(username string) error
addNode() error
getNodeByName(name string) (Node, error)
getNodes() ([]Node, error)
updateNodeTimestamp() error
cleanupNodes() error
checkAvailability() error
close() error
reloadConfig() error
@@ -801,7 +811,6 @@ func checkSharedMode() {
// Initialize the data provider.
// An error is returned if the configured driver is invalid or if the data provider cannot be initialized
func Initialize(cnf Config, basePath string, checkAdmins bool) error {
var err error
config = cnf
checkSharedMode()
config.Actions.ExecuteOn = util.RemoveDuplicates(config.Actions.ExecuteOn, true)
@@ -812,19 +821,33 @@ func Initialize(cnf Config, basePath string, checkAdmins bool) error {
return fmt.Errorf("required directory is invalid, backup path %#v", cnf.BackupsPath)
}
if err = initializeHashingAlgo(&cnf); err != nil {
if err := initializeHashingAlgo(&cnf); err != nil {
return err
}
if err = validateHooks(); err != nil {
if err := validateHooks(); err != nil {
return err
}
err = createProvider(basePath)
if err := createProvider(basePath); err != nil {
return err
}
if err := checkDatabase(checkAdmins); err != nil {
return err
}
admins, err := provider.getAdmins(1, 0, OrderASC)
if err != nil {
return err
}
if cnf.UpdateMode == 0 {
err = provider.initializeDatabase()
isAdminCreated.Store(len(admins) > 0)
if err := config.Node.validate(); err != nil {
return err
}
delayedQuotaUpdater.start()
return startScheduler()
}
func checkDatabase(checkAdmins bool) error {
if config.UpdateMode == 0 {
err := provider.initializeDatabase()
if err != nil && err != ErrNoInitRequired {
logger.WarnToConsole("Unable to initialize data provider: %v", err)
providerLog(logger.LevelError, "Unable to initialize data provider: %v", err)
@@ -838,7 +861,7 @@ func Initialize(cnf Config, basePath string, checkAdmins bool) error {
providerLog(logger.LevelError, "database migration error: %v", err)
return err
}
if checkAdmins && cnf.CreateDefaultAdmin {
if checkAdmins && config.CreateDefaultAdmin {
err = checkDefaultAdmin()
if err != nil {
providerLog(logger.LevelError, "erro checking the default admin: %v", err)
@@ -848,13 +871,7 @@ func Initialize(cnf Config, basePath string, checkAdmins bool) error {
} else {
providerLog(logger.LevelInfo, "database initialization/migration skipped, manual mode is configured")
}
admins, err := provider.getAdmins(1, 0, OrderASC)
if err != nil {
return err
}
isAdminCreated.Store(len(admins) > 0)
delayedQuotaUpdater.start()
return startScheduler()
return nil
}
func validateHooks() error {
@@ -937,15 +954,17 @@ func validateSQLTablesPrefix() error {
sqlTableEventsRules = config.SQLTablesPrefix + sqlTableEventsRules
sqlTableRulesActionsMapping = config.SQLTablesPrefix + sqlTableRulesActionsMapping
sqlTableTasks = config.SQLTablesPrefix + sqlTableTasks
sqlTableNodes = config.SQLTablesPrefix + sqlTableNodes
sqlTableSchemaVersion = config.SQLTablesPrefix + sqlTableSchemaVersion
providerLog(logger.LevelDebug, "sql table for users %q, folders %q users folders mapping %q admins %q "+
"api keys %q shares %q defender hosts %q defender events %q transfers %q groups %q "+
"users groups mapping %q admins groups mapping %q groups folders mapping %q shared sessions %q "+
"schema version %q events actions %q events rules %q rules actions mapping %q tasks %q",
"schema version %q events actions %q events rules %q rules actions mapping %q tasks %q nodes %q",
sqlTableUsers, sqlTableFolders, sqlTableUsersFoldersMapping, sqlTableAdmins, sqlTableAPIKeys,
sqlTableShares, sqlTableDefenderHosts, sqlTableDefenderEvents, sqlTableActiveTransfers, sqlTableGroups,
sqlTableUsersGroupsMapping, sqlTableAdminsGroupsMapping, sqlTableGroupsFoldersMapping, sqlTableSharedSessions,
sqlTableSchemaVersion, sqlTableEventsActions, sqlTableEventsRules, sqlTableRulesActionsMapping, sqlTableTasks)
sqlTableSchemaVersion, sqlTableEventsActions, sqlTableEventsRules, sqlTableRulesActionsMapping,
sqlTableTasks, sqlTableNodes)
}
return nil
}
@@ -1728,6 +1747,29 @@ func UpdateTaskTimestamp(name string) error {
return provider.updateTaskTimestamp(name)
}
// GetNodes returns the other cluster nodes
func GetNodes() ([]Node, error) {
if currentNode == nil {
return nil, nil
}
nodes, err := provider.getNodes()
if err != nil {
providerLog(logger.LevelError, "unable to get other cluster nodes %v", err)
}
return nodes, err
}
// GetNodeByName returns a node, different from the current one, by name
func GetNodeByName(name string) (Node, error) {
if currentNode == nil {
return Node{}, util.NewRecordNotFoundError(errNoClusterNodes.Error())
}
if name == currentNode.Name {
return Node{}, util.NewValidationError(fmt.Sprintf("%s is the current node, it must refer to other nodes", name))
}
return provider.getNodeByName(name)
}
// HasAdmin returns true if the first admin has been created
// and so SFTPGo is ready to be used
func HasAdmin() bool {