mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-22 02:50:04 -05:00
cmd/dolt: Implement new sql-server.lock semantics. Assert sql-server has write access to the databases it loads when it starts, look for the credentials file in parent directories.
This commit is contained in:
@@ -15,8 +15,10 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"errors"
|
||||
"fmt"
|
||||
iofs "io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -83,8 +85,43 @@ func WriteLocalCreds(fs filesys.Filesys, creds *LocalCreds) error {
|
||||
return fs.WriteFile(credsFile, []byte(fmt.Sprintf("%d:%s:%s", creds.Pid, portStr, creds.Secret)), 0600)
|
||||
}
|
||||
|
||||
func LoadLocalCreds(fs filesys.Filesys, credsFilePath string) (creds *LocalCreds, err error) {
|
||||
rd, err := fs.OpenForRead(credsFilePath)
|
||||
// Starting at `fs`, look for the a ServerLocalCredsFile in the .dolt directory
|
||||
// of this directory and every parent directory, until we find one. When we
|
||||
// find we, we return its contents if we can open and parse it successfully.
|
||||
// Otherwise, we return an error associated with attempting to read it. If we
|
||||
// do not find anything all the way up to the root of the filesystem, returns
|
||||
// `nil` *LocalCreds and a `nil` error.
|
||||
func FindAndLoadLocalCreds(fs filesys.Filesys) (creds *LocalCreds, err error) {
|
||||
root, err := fs.Abs(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for root != "" && root[len(root)-1] != '/' {
|
||||
creds, err := LoadLocalCreds(fs)
|
||||
if err == nil {
|
||||
return creds, err
|
||||
}
|
||||
// If we have an error that is not ErrNotExist, for example, a
|
||||
// permission error opening the credentials file, or an error
|
||||
// indicating that the contents of the file were malformed, go
|
||||
// ahead and return the error and terminate our search here.
|
||||
if !errors.Is(err, iofs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
fs, err = fs.WithWorkingDir("..")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err = fs.Abs(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func LoadLocalCreds(fs filesys.Filesys) (creds *LocalCreds, err error) {
|
||||
rd, err := fs.OpenForRead(filepath.Join(dbfactory.DoltDir, ServerLocalCredsFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ const (
|
||||
// but will break compatibility with implementing applications that do not yet support users.
|
||||
var ExternalDisableUsers bool = false
|
||||
|
||||
var ErrCouldNotLockDatabase = goerrors.NewKind("database \"%s\" is locked by another dolt process; either clone the database to run a second server, or stop the dolt process which currently holds an exclusive write lock on the database")
|
||||
|
||||
// Serve starts a MySQL-compatible server. Returns any errors that were encountered.
|
||||
func Serve(
|
||||
ctx context.Context,
|
||||
@@ -148,8 +150,12 @@ func Serve(
|
||||
|
||||
AssertNoDatabasesInAccessModeReadOnly := &svcs.AnonService{
|
||||
InitF: func(ctx context.Context) (err error) {
|
||||
// TODO: Iterate mrEnv, assert env.DoltDB.AccessMode() != chunks.ExclusiveAccessMode_ReadOnly
|
||||
return nil
|
||||
return mrEnv.Iter(func(name string, dEnv *env.DoltEnv) (stop bool, err error) {
|
||||
if dEnv.IsAccessModeReadOnly() {
|
||||
return true, ErrCouldNotLockDatabase.New(name)
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
},
|
||||
}
|
||||
controller.Register(AssertNoDatabasesInAccessModeReadOnly)
|
||||
|
||||
@@ -74,7 +74,7 @@ func (cmd StashDropCmd) Exec(ctx context.Context, commandStr string, args []stri
|
||||
ap := cmd.ArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashDropDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
|
||||
|
||||
// TODO: Error if dEnv.DoltDB.AccessMode() == ReadOnly?
|
||||
|
||||
var idx = 0
|
||||
|
||||
+15
-14
@@ -701,23 +701,24 @@ If you're interested in running this command against a remote host, hit us up on
|
||||
targetEnv = rootEnv
|
||||
}
|
||||
|
||||
// TODO: if targetEnv.DoltDB.AccessMode() == ReadOnly...
|
||||
// Need to go looking for local creds to use...
|
||||
var isLocked bool
|
||||
var lock *sqlserver.LocalCreds
|
||||
var err error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isLocked {
|
||||
if verbose {
|
||||
cli.Println("verbose: starting remote mode")
|
||||
// If the loaded environment does not itself have a DoltDB, then we look for a server.
|
||||
// We also look for a server when the loaded environment has a DoltDB but another
|
||||
// running process already has exclusive write access to it.
|
||||
if targetEnv.DoltDB == nil || targetEnv.IsAccessModeReadOnly() {
|
||||
localCreds, err := sqlserver.FindAndLoadLocalCreds(targetEnv.FS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if localCreds != nil {
|
||||
if verbose {
|
||||
cli.Println("verbose: starting remote mode")
|
||||
}
|
||||
|
||||
if !creds.Specified {
|
||||
creds = &cli.UserPassword{Username: sqlserver.LocalConnectionUser, Password: lock.Secret, Specified: false}
|
||||
if !creds.Specified {
|
||||
creds = &cli.UserPassword{Username: sqlserver.LocalConnectionUser, Password: localCreds.Secret, Specified: false}
|
||||
}
|
||||
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, "localhost", localCreds.Port, false, useDb)
|
||||
}
|
||||
return sqlserver.BuildConnectionStringQueryist(ctx, cwdFS, creds, apr, "localhost", lock.Port, false, useDb)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
|
||||
@@ -131,6 +131,10 @@ func (ddb *DoltDB) NomsRoot(ctx context.Context) (hash.Hash, error) {
|
||||
return datas.ChunkStoreFromDatabase(ddb.db).Root(ctx)
|
||||
}
|
||||
|
||||
func (ddb *DoltDB) AccessMode() chunks.ExclusiveAccessMode {
|
||||
return datas.ChunkStoreFromDatabase(ddb.db).AccessMode()
|
||||
}
|
||||
|
||||
// CommitRoot executes a chunkStore commit, atomically swapping the root hash of the database manifest
|
||||
func (ddb *DoltDB) CommitRoot(ctx context.Context, last, current hash.Hash) (bool, error) {
|
||||
return datas.ChunkStoreFromDatabase(ddb.db).Commit(ctx, last, current)
|
||||
|
||||
+5
-2
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/dolthub/dolt/go/libraries/utils/concurrentmap"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/config"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
||||
"github.com/dolthub/dolt/go/store/chunks"
|
||||
"github.com/dolthub/dolt/go/store/datas"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
@@ -52,8 +53,6 @@ const (
|
||||
DefaultRemotesApiPort = "443"
|
||||
|
||||
tempTablesDir = "temptf"
|
||||
|
||||
ServerLockFile = "sql-server.lock"
|
||||
)
|
||||
|
||||
var zeroHashStr = (hash.Hash{}).String()
|
||||
@@ -1183,3 +1182,7 @@ func (dEnv *DoltEnv) BulkDbEaFactory() editor.DbEaFactory {
|
||||
}
|
||||
return editor.NewBulkImportTEAFactory(dEnv.DoltDB.ValueReadWriter(), tmpDir)
|
||||
}
|
||||
|
||||
func (dEnv *DoltEnv) IsAccessModeReadOnly() bool {
|
||||
return dEnv.DoltDB.AccessMode() == chunks.ExclusiveAccessMode_ReadOnly
|
||||
}
|
||||
|
||||
+8
-11
@@ -22,7 +22,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/src-d/go-errors.v1"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
@@ -32,8 +31,6 @@ import (
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
)
|
||||
|
||||
var ErrActiveServerLock = errors.NewKind("database locked by another sql-server; either clone the database to run a second server, or delete the '%s' if no other sql-servers are active")
|
||||
|
||||
// EnvNameAndPath is a simple tuple of the name of an environment and the path to where it is on disk
|
||||
type EnvNameAndPath struct {
|
||||
// Name is the name of the environment and is used as the identifier when accessing a given environment
|
||||
@@ -49,10 +46,10 @@ type NamedEnv struct {
|
||||
|
||||
// MultiRepoEnv is a type used to store multiple environments which can be retrieved by name
|
||||
type MultiRepoEnv struct {
|
||||
envs []NamedEnv
|
||||
fs filesys.Filesys
|
||||
cfg config.ReadWriteConfig
|
||||
dialProvider dbfactory.GRPCDialProvider
|
||||
envs []NamedEnv
|
||||
fs filesys.Filesys
|
||||
cfg config.ReadWriteConfig
|
||||
dialProvider dbfactory.GRPCDialProvider
|
||||
}
|
||||
|
||||
// NewMultiEnv returns a new MultiRepoEnv instance dirived from a root DoltEnv instance.
|
||||
@@ -88,10 +85,10 @@ func MultiEnvForDirectory(
|
||||
}
|
||||
|
||||
mrEnv := &MultiRepoEnv{
|
||||
envs: make([]NamedEnv, 0),
|
||||
fs: dataDirFS,
|
||||
cfg: config,
|
||||
dialProvider: NewGRPCDialProviderFromDoltEnv(newDEnv),
|
||||
envs: make([]NamedEnv, 0),
|
||||
fs: dataDirFS,
|
||||
cfg: config,
|
||||
dialProvider: NewGRPCDialProviderFromDoltEnv(newDEnv),
|
||||
}
|
||||
|
||||
envSet := map[string]*DoltEnv{}
|
||||
|
||||
Reference in New Issue
Block a user