github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/env/environment.go (about)

     1  // Copyright 2019-2020 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package env
    16  
    17  import (
    18  	"context"
    19  	"crypto/tls"
    20  	"errors"
    21  	"fmt"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  	"time"
    26  	"unicode"
    27  
    28  	"google.golang.org/grpc"
    29  	"google.golang.org/grpc/credentials"
    30  
    31  	"github.com/dolthub/dolt/go/cmd/dolt/errhand"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/creds"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
    34  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    35  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdocs"
    36  	"github.com/dolthub/dolt/go/libraries/doltcore/grpcendpoint"
    37  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    38  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    39  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding"
    40  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    41  	"github.com/dolthub/dolt/go/store/hash"
    42  	"github.com/dolthub/dolt/go/store/types"
    43  )
    44  
    45  const (
    46  	DefaultLoginUrl       = "https://dolthub.com/settings/credentials"
    47  	DefaultMetricsHost    = "eventsapi.dolthub.com"
    48  	DefaultMetricsPort    = "443"
    49  	DefaultRemotesApiHost = "doltremoteapi.dolthub.com"
    50  	DefaultRemotesApiPort = "443"
    51  	tempTablesDir         = "temptf"
    52  )
    53  
    54  var ErrPreexistingDoltDir = errors.New(".dolt dir already exists")
    55  var ErrStateUpdate = errors.New("error updating local data repo state")
    56  var ErrMarshallingSchema = errors.New("error marshalling schema")
    57  var ErrInvalidCredsFile = errors.New("invalid creds file")
    58  
    59  // DoltEnv holds the state of the current environment used by the cli.
    60  type DoltEnv struct {
    61  	Version string
    62  
    63  	Config     *DoltCliConfig
    64  	CfgLoadErr error
    65  
    66  	RepoState *RepoState
    67  	RSLoadErr error
    68  
    69  	Docs        doltdocs.Docs
    70  	DocsLoadErr error
    71  
    72  	DoltDB      *doltdb.DoltDB
    73  	DBLoadError error
    74  
    75  	FS     filesys.Filesys
    76  	urlStr string
    77  	hdp    HomeDirProvider
    78  }
    79  
    80  // Load loads the DoltEnv for the current directory of the cli
    81  func Load(ctx context.Context, hdp HomeDirProvider, fs filesys.Filesys, urlStr, version string) *DoltEnv {
    82  	config, cfgErr := loadDoltCliConfig(hdp, fs)
    83  	repoState, rsErr := LoadRepoState(fs)
    84  	docs, docsErr := doltdocs.LoadDocs(fs)
    85  	ddb, dbLoadErr := doltdb.LoadDoltDB(ctx, types.Format_Default, urlStr)
    86  
    87  	dEnv := &DoltEnv{
    88  		version,
    89  		config,
    90  		cfgErr,
    91  		repoState,
    92  		rsErr,
    93  		docs,
    94  		docsErr,
    95  		ddb,
    96  		dbLoadErr,
    97  		fs,
    98  		urlStr,
    99  		hdp,
   100  	}
   101  
   102  	if dbLoadErr == nil && dEnv.HasDoltDir() {
   103  		if !dEnv.HasDoltTempTableDir() {
   104  			err := dEnv.FS.MkDirs(dEnv.TempTableFilesDir())
   105  			dEnv.DBLoadError = err
   106  		} else {
   107  			// fire and forget cleanup routine.  Will delete as many old temp files as it can during the main commands execution.
   108  			// The process will not wait for this to finish so this may not always complete.
   109  			go func() {
   110  				_ = fs.Iter(dEnv.TempTableFilesDir(), true, func(path string, size int64, isDir bool) (stop bool) {
   111  					if !isDir {
   112  						lm, exists := fs.LastModified(path)
   113  
   114  						if exists && time.Now().Sub(lm) > (time.Hour*24) {
   115  							_ = fs.DeleteFile(path)
   116  						}
   117  					}
   118  
   119  					return false
   120  				})
   121  			}()
   122  		}
   123  	}
   124  
   125  	dbfactory.InitializeFactories(dEnv)
   126  
   127  	return dEnv
   128  }
   129  
   130  // HasDoltDir returns true if the .dolt directory exists and is a valid directory
   131  func (dEnv *DoltEnv) HasDoltDir() bool {
   132  	return dEnv.hasDoltDir("./")
   133  }
   134  
   135  func (dEnv *DoltEnv) HasDoltDataDir() bool {
   136  	exists, isDir := dEnv.FS.Exists(dbfactory.DoltDataDir)
   137  	return exists && isDir
   138  }
   139  
   140  func (dEnv *DoltEnv) HasDoltTempTableDir() bool {
   141  	ex, _ := dEnv.FS.Exists(dEnv.TempTableFilesDir())
   142  
   143  	return ex
   144  }
   145  
   146  func mustAbs(dEnv *DoltEnv, path ...string) string {
   147  	absPath, err := dEnv.FS.Abs(filepath.Join(path...))
   148  
   149  	if err != nil {
   150  		panic(err)
   151  	}
   152  
   153  	return absPath
   154  }
   155  
   156  // GetDoltDir returns the path to the .dolt directory
   157  func (dEnv *DoltEnv) GetDoltDir() string {
   158  	if !dEnv.HasDoltDataDir() {
   159  		panic("No dolt dir")
   160  	}
   161  
   162  	return mustAbs(dEnv, dbfactory.DoltDir)
   163  }
   164  
   165  func (dEnv *DoltEnv) hasDoltDir(path string) bool {
   166  	exists, isDir := dEnv.FS.Exists(mustAbs(dEnv, dbfactory.DoltDir))
   167  	return exists && isDir
   168  }
   169  
   170  // HasLocalConfig returns true if a repository local config file
   171  func (dEnv *DoltEnv) HasLocalConfig() bool {
   172  	_, ok := dEnv.Config.GetConfig(LocalConfig)
   173  
   174  	return ok
   175  }
   176  
   177  func (dEnv *DoltEnv) bestEffortDeleteAll(dir string) {
   178  	fileToIsDir := make(map[string]bool)
   179  	dEnv.FS.Iter(dir, false, func(path string, size int64, isDir bool) (stop bool) {
   180  		fileToIsDir[path] = isDir
   181  		return false
   182  	})
   183  
   184  	for path, isDir := range fileToIsDir {
   185  		if isDir {
   186  			dEnv.FS.Delete(path, true)
   187  		} else {
   188  			dEnv.FS.DeleteFile(path)
   189  		}
   190  	}
   191  }
   192  
   193  // InitRepo takes an empty directory and initializes it with a .dolt directory containing repo state, uncommitted license and readme, and creates a noms
   194  // database with dolt structure.
   195  func (dEnv *DoltEnv) InitRepo(ctx context.Context, nbf *types.NomsBinFormat, name, email string) error { // should remove name and email args
   196  	return dEnv.InitRepoWithTime(ctx, nbf, name, email, doltdb.CommitNowFunc())
   197  }
   198  
   199  func (dEnv *DoltEnv) InitRepoWithTime(ctx context.Context, nbf *types.NomsBinFormat, name, email string, t time.Time) error { // should remove name and email args
   200  	doltDir, err := dEnv.createDirectories(".")
   201  
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	err = dEnv.configureRepo(doltDir)
   207  
   208  	if err == nil {
   209  		err = dEnv.InitDBAndRepoState(ctx, nbf, name, email, t)
   210  	}
   211  
   212  	if err != nil {
   213  		dEnv.bestEffortDeleteAll(dbfactory.DoltDir)
   214  	}
   215  
   216  	return err
   217  }
   218  
   219  func (dEnv *DoltEnv) InitRepoWithNoData(ctx context.Context, nbf *types.NomsBinFormat) error {
   220  	doltDir, err := dEnv.createDirectories(".")
   221  
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	err = dEnv.configureRepo(doltDir)
   227  
   228  	if err != nil {
   229  		dEnv.bestEffortDeleteAll(dbfactory.DoltDir)
   230  		return err
   231  	}
   232  
   233  	dEnv.DoltDB, err = doltdb.LoadDoltDB(ctx, nbf, dEnv.urlStr)
   234  
   235  	return err
   236  }
   237  
   238  func (dEnv *DoltEnv) createDirectories(dir string) (string, error) {
   239  	absPath, err := dEnv.FS.Abs(dir)
   240  
   241  	if err != nil {
   242  		return "", err
   243  	}
   244  
   245  	exists, isDir := dEnv.FS.Exists(absPath)
   246  
   247  	if !exists {
   248  		return "", fmt.Errorf("'%s' does not exist so could not create '%s", absPath, dbfactory.DoltDataDir)
   249  	} else if !isDir {
   250  		return "", fmt.Errorf("'%s' exists but it's a file not a directory", absPath)
   251  	}
   252  
   253  	if dEnv.hasDoltDir(dir) {
   254  		return "", ErrPreexistingDoltDir
   255  	}
   256  
   257  	absDataDir := filepath.Join(absPath, dbfactory.DoltDataDir)
   258  	err = dEnv.FS.MkDirs(absDataDir)
   259  
   260  	if err != nil {
   261  		return "", fmt.Errorf("unable to make directory '%s', cause: %s", absDataDir, err.Error())
   262  	}
   263  
   264  	err = dEnv.FS.MkDirs(dEnv.TempTableFilesDir())
   265  
   266  	if err != nil {
   267  		return "", fmt.Errorf("unable to make directory '%s', cause: %s", dEnv.TempTableFilesDir(), err.Error())
   268  	}
   269  
   270  	return filepath.Join(absPath, dbfactory.DoltDir), nil
   271  }
   272  
   273  func (dEnv *DoltEnv) configureRepo(doltDir string) error {
   274  	err := dEnv.Config.CreateLocalConfig(map[string]string{})
   275  
   276  	if err != nil {
   277  		return fmt.Errorf("failed creating file %s", getLocalConfigPath())
   278  	}
   279  
   280  	return nil
   281  }
   282  
   283  // Inits the dolt DB of this environment with an empty commit at the time given and writes default docs to disk.
   284  // Writes new repo state with a master branch and current root hash.
   285  func (dEnv *DoltEnv) InitDBAndRepoState(ctx context.Context, nbf *types.NomsBinFormat, name, email string, t time.Time) error {
   286  	err := dEnv.InitDBWithTime(ctx, nbf, name, email, t)
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	return dEnv.InitializeRepoState(ctx)
   292  }
   293  
   294  // Inits the dolt DB of this environment with an empty commit at the time given and writes default docs to disk.
   295  // Does not update repo state.
   296  func (dEnv *DoltEnv) InitDBWithTime(ctx context.Context, nbf *types.NomsBinFormat, name, email string, t time.Time) error {
   297  	var err error
   298  	dEnv.DoltDB, err = doltdb.LoadDoltDB(ctx, nbf, dEnv.urlStr)
   299  
   300  	if err != nil {
   301  		return err
   302  	}
   303  
   304  	err = dEnv.DoltDB.WriteEmptyRepoWithCommitTime(ctx, name, email, t)
   305  	if err != nil {
   306  		return doltdb.ErrNomsIO
   307  	}
   308  
   309  	return nil
   310  }
   311  
   312  // InitializeRepoState writes a default repo state to disk, consisting of a master branch and current root hash value.
   313  func (dEnv *DoltEnv) InitializeRepoState(ctx context.Context) error {
   314  	cs, _ := doltdb.NewCommitSpec(doltdb.MasterBranch)
   315  	commit, err := dEnv.DoltDB.Resolve(ctx, cs, nil)
   316  	if err != nil {
   317  		return err
   318  	}
   319  
   320  	root, err := commit.GetRootValue()
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	rootHash, err := root.HashOf()
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	dEnv.RepoState, err = CreateRepoState(dEnv.FS, doltdb.MasterBranch, rootHash)
   331  	if err != nil {
   332  		return ErrStateUpdate
   333  	}
   334  
   335  	dEnv.RSLoadErr = nil
   336  	return nil
   337  }
   338  
   339  func (dEnv *DoltEnv) WorkingRoot(ctx context.Context) (*doltdb.RootValue, error) {
   340  	return dEnv.DoltDB.ReadRootValue(ctx, dEnv.RepoState.WorkingHash())
   341  }
   342  
   343  func (dEnv *DoltEnv) UpdateWorkingRoot(ctx context.Context, newRoot *doltdb.RootValue) error {
   344  	h, err := dEnv.DoltDB.WriteRootValue(ctx, newRoot)
   345  
   346  	if err != nil {
   347  		return doltdb.ErrNomsIO
   348  	}
   349  
   350  	return dEnv.RepoStateWriter().SetWorkingHash(ctx, h)
   351  }
   352  
   353  type repoStateReader struct {
   354  	dEnv *DoltEnv
   355  }
   356  
   357  func (r *repoStateReader) CWBHeadRef() ref.DoltRef {
   358  	return r.dEnv.RepoState.CWBHeadRef()
   359  }
   360  
   361  func (r *repoStateReader) CWBHeadSpec() *doltdb.CommitSpec {
   362  	return r.dEnv.RepoState.CWBHeadSpec()
   363  }
   364  
   365  func (r *repoStateReader) CWBHeadHash(ctx context.Context) (hash.Hash, error) {
   366  	ref := r.CWBHeadRef()
   367  	cm, err := r.dEnv.DoltDB.ResolveCommitRef(ctx, ref)
   368  
   369  	if err != nil {
   370  		return hash.Hash{}, err
   371  	}
   372  
   373  	return cm.HashOf()
   374  }
   375  
   376  func (r *repoStateReader) WorkingHash() hash.Hash {
   377  	return r.dEnv.RepoState.WorkingHash()
   378  }
   379  
   380  func (r *repoStateReader) StagedHash() hash.Hash {
   381  	return hash.Parse(r.dEnv.RepoState.Staged)
   382  }
   383  
   384  func (r *repoStateReader) IsMergeActive() bool {
   385  	return r.dEnv.RepoState.Merge != nil
   386  }
   387  
   388  func (r *repoStateReader) GetMergeCommit() string {
   389  	return r.dEnv.RepoState.Merge.Commit
   390  }
   391  
   392  func (r *repoStateReader) GetPreMergeWorking() string {
   393  	return r.dEnv.RepoState.Merge.PreMergeWorking
   394  }
   395  
   396  func (dEnv *DoltEnv) RepoStateReader() RepoStateReader {
   397  	return &repoStateReader{dEnv}
   398  }
   399  
   400  type repoStateWriter struct {
   401  	dEnv *DoltEnv
   402  }
   403  
   404  func (r *repoStateWriter) SetStagedHash(ctx context.Context, h hash.Hash) error {
   405  	r.dEnv.RepoState.Staged = h.String()
   406  	err := r.dEnv.RepoState.Save(r.dEnv.FS)
   407  
   408  	if err != nil {
   409  		return ErrStateUpdate
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  func (r *repoStateWriter) SetWorkingHash(ctx context.Context, h hash.Hash) error {
   416  	r.dEnv.RepoState.Working = h.String()
   417  	err := r.dEnv.RepoState.Save(r.dEnv.FS)
   418  
   419  	if err != nil {
   420  		return ErrStateUpdate
   421  	}
   422  
   423  	return nil
   424  }
   425  
   426  func (r *repoStateWriter) SetCWBHeadRef(ctx context.Context, marshalableRef ref.MarshalableRef) error {
   427  	r.dEnv.RepoState.Head = marshalableRef
   428  	err := r.dEnv.RepoState.Save(r.dEnv.FS)
   429  
   430  	if err != nil {
   431  		return ErrStateUpdate
   432  	}
   433  
   434  	return nil
   435  }
   436  
   437  func (r *repoStateWriter) AbortMerge() error {
   438  	return r.dEnv.RepoState.AbortMerge(r.dEnv.FS)
   439  }
   440  
   441  func (r *repoStateWriter) ClearMerge() error {
   442  	return r.dEnv.RepoState.ClearMerge(r.dEnv.FS)
   443  }
   444  
   445  func (r *repoStateWriter) StartMerge(commitStr string) error {
   446  	return r.dEnv.RepoState.StartMerge(commitStr, r.dEnv.FS)
   447  }
   448  
   449  func (dEnv *DoltEnv) RepoStateWriter() RepoStateWriter {
   450  	return &repoStateWriter{dEnv}
   451  }
   452  
   453  type docsReadWriter struct {
   454  	FS filesys.Filesys
   455  }
   456  
   457  // GetDocsOnDisk reads the filesystem and returns all docs.
   458  func (d *docsReadWriter) GetDocsOnDisk(docNames ...string) (doltdocs.Docs, error) {
   459  	if docNames != nil {
   460  		ret := make(doltdocs.Docs, len(docNames))
   461  
   462  		for i, name := range docNames {
   463  			doc, err := doltdocs.GetDoc(d.FS, name)
   464  			if err != nil {
   465  				return nil, err
   466  			}
   467  			ret[i] = doc
   468  		}
   469  
   470  		return ret, nil
   471  	}
   472  
   473  	return doltdocs.GetSupportedDocs(d.FS)
   474  }
   475  
   476  // WriteDocsToDisk creates or updates the dolt_docs table with docs.
   477  func (d *docsReadWriter) WriteDocsToDisk(docs doltdocs.Docs) error {
   478  	return docs.Save(d.FS)
   479  }
   480  
   481  func (dEnv *DoltEnv) DocsReadWriter() DocsReadWriter {
   482  	return &docsReadWriter{dEnv.FS}
   483  }
   484  
   485  func (dEnv *DoltEnv) HeadRoot(ctx context.Context) (*doltdb.RootValue, error) {
   486  	commit, err := dEnv.DoltDB.ResolveCommitRef(ctx, dEnv.RepoState.CWBHeadRef())
   487  
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  
   492  	return commit.GetRootValue()
   493  }
   494  
   495  func (dEnv *DoltEnv) DbData() DbData {
   496  	return DbData{
   497  		Ddb: dEnv.DoltDB,
   498  		Rsw: dEnv.RepoStateWriter(),
   499  		Rsr: dEnv.RepoStateReader(),
   500  		Drw: dEnv.DocsReadWriter(),
   501  	}
   502  }
   503  
   504  func (dEnv *DoltEnv) StagedRoot(ctx context.Context) (*doltdb.RootValue, error) {
   505  	return dEnv.DoltDB.ReadRootValue(ctx, dEnv.RepoState.StagedHash())
   506  }
   507  
   508  func (dEnv *DoltEnv) UpdateStagedRoot(ctx context.Context, newRoot *doltdb.RootValue) (hash.Hash, error) {
   509  	h, err := dEnv.DoltDB.WriteRootValue(ctx, newRoot)
   510  
   511  	if err != nil {
   512  		return hash.Hash{}, doltdb.ErrNomsIO
   513  	}
   514  
   515  	dEnv.RepoState.Staged = h.String()
   516  	err = dEnv.RepoState.Save(dEnv.FS)
   517  
   518  	if err != nil {
   519  		return hash.Hash{}, ErrStateUpdate
   520  	}
   521  
   522  	return h, nil
   523  }
   524  
   525  // todo: move this out of env to actions
   526  func (dEnv *DoltEnv) PutTableToWorking(ctx context.Context, sch schema.Schema, rows types.Map, indexData types.Map, tableName string, autoVal types.Value) error {
   527  	root, err := dEnv.WorkingRoot(ctx)
   528  	if err != nil {
   529  		return doltdb.ErrNomsIO
   530  	}
   531  
   532  	vrw := dEnv.DoltDB.ValueReadWriter()
   533  	schVal, err := encoding.MarshalSchemaAsNomsValue(ctx, vrw, sch)
   534  	if err != nil {
   535  		return ErrMarshallingSchema
   536  	}
   537  
   538  	tbl, err := doltdb.NewTable(ctx, vrw, schVal, rows, indexData, autoVal)
   539  	if err != nil {
   540  		return err
   541  	}
   542  
   543  	newRoot, err := root.PutTable(ctx, tableName, tbl)
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	rootHash, err := root.HashOf()
   549  	if err != nil {
   550  		return err
   551  	}
   552  
   553  	newRootHash, err := newRoot.HashOf()
   554  	if err != nil {
   555  		return err
   556  	}
   557  	if rootHash == newRootHash {
   558  		return nil
   559  	}
   560  
   561  	return dEnv.UpdateWorkingRoot(ctx, newRoot)
   562  }
   563  
   564  func (dEnv *DoltEnv) IsMergeActive() bool {
   565  	return dEnv.RepoState.Merge != nil
   566  }
   567  
   568  func (dEnv *DoltEnv) GetTablesWithConflicts(ctx context.Context) ([]string, error) {
   569  	root, err := dEnv.WorkingRoot(ctx)
   570  
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  
   575  	return root.TablesInConflict(ctx)
   576  }
   577  
   578  func (dEnv *DoltEnv) CredsDir() (string, error) {
   579  	return getCredsDir(dEnv.hdp)
   580  }
   581  
   582  func (dEnv *DoltEnv) UserRPCCreds() (creds.DoltCreds, bool, error) {
   583  	kid, err := dEnv.Config.GetString(UserCreds)
   584  
   585  	if err == nil && kid != "" {
   586  		dir, err := dEnv.CredsDir()
   587  
   588  		if err != nil {
   589  			// not sure why you wouldn't be able to get the creds dir.
   590  			panic(err)
   591  		}
   592  
   593  		c, err := creds.JWKCredsReadFromFile(dEnv.FS, filepath.Join(dir, kid+".jwk"))
   594  		return c, c.IsPrivKeyValid() && c.IsPubKeyValid(), err
   595  	}
   596  
   597  	return creds.EmptyCreds, false, nil
   598  }
   599  
   600  func (dEnv *DoltEnv) getRPCCreds() (credentials.PerRPCCredentials, error) {
   601  	dCreds, valid, err := dEnv.UserRPCCreds()
   602  	if err != nil {
   603  		return nil, ErrInvalidCredsFile
   604  	}
   605  	if !valid {
   606  		return nil, nil
   607  	}
   608  	return dCreds, nil
   609  }
   610  
   611  func (dEnv *DoltEnv) getUserAgentString() string {
   612  	tokens := []string{
   613  		"dolt_cli",
   614  		dEnv.Version,
   615  		runtime.GOOS,
   616  		runtime.GOARCH,
   617  	}
   618  
   619  	for i, t := range tokens {
   620  		tokens[i] = strings.Map(func(r rune) rune {
   621  			if unicode.IsSpace(r) {
   622  				return '_'
   623  			}
   624  
   625  			return r
   626  		}, strings.TrimSpace(t))
   627  	}
   628  
   629  	return strings.Join(tokens, " ")
   630  }
   631  
   632  func (dEnv *DoltEnv) GetGRPCDialParams(config grpcendpoint.Config) (string, []grpc.DialOption, error) {
   633  	endpoint := config.Endpoint
   634  	if strings.IndexRune(endpoint, ':') == -1 {
   635  		if config.Insecure {
   636  			endpoint += ":80"
   637  		} else {
   638  			endpoint += ":443"
   639  		}
   640  	}
   641  
   642  	var opts []grpc.DialOption
   643  	if config.Insecure {
   644  		opts = append(opts, grpc.WithInsecure())
   645  	} else {
   646  		tc := credentials.NewTLS(&tls.Config{})
   647  		opts = append(opts, grpc.WithTransportCredentials(tc))
   648  	}
   649  
   650  	opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(128*1024*1024)))
   651  	opts = append(opts, grpc.WithUserAgent(dEnv.getUserAgentString()))
   652  
   653  	if config.Creds != nil {
   654  		opts = append(opts, grpc.WithPerRPCCredentials(config.Creds))
   655  	} else if config.WithEnvCreds {
   656  		rpcCreds, err := dEnv.getRPCCreds()
   657  		if err != nil {
   658  			return "", nil, err
   659  		}
   660  		if rpcCreds != nil {
   661  			opts = append(opts, grpc.WithPerRPCCredentials(rpcCreds))
   662  		}
   663  	}
   664  
   665  	return endpoint, opts, nil
   666  }
   667  
   668  func (dEnv *DoltEnv) GetRemotes() (map[string]Remote, error) {
   669  	if dEnv.RSLoadErr != nil {
   670  		return nil, dEnv.RSLoadErr
   671  	}
   672  
   673  	return dEnv.RepoState.Remotes, nil
   674  }
   675  
   676  var ErrNotACred = errors.New("not a valid credential key id or public key")
   677  
   678  func (dEnv *DoltEnv) FindCreds(credsDir, pubKeyOrId string) (string, error) {
   679  	if !creds.B32CredsByteSet.ContainsAll([]byte(pubKeyOrId)) {
   680  		return "", creds.ErrBadB32CredsEncoding
   681  	}
   682  
   683  	if len(pubKeyOrId) == creds.B32EncodedPubKeyLen {
   684  		pubKeyOrId, _ = creds.PubKeyStrToKIDStr(pubKeyOrId)
   685  	}
   686  
   687  	if len(pubKeyOrId) != creds.B32EncodedKeyIdLen {
   688  		return "", ErrNotACred
   689  	}
   690  
   691  	path := mustAbs(dEnv, credsDir, pubKeyOrId+creds.JWKFileExtension)
   692  	exists, isDir := dEnv.FS.Exists(path)
   693  
   694  	if isDir {
   695  		return path, filesys.ErrIsDir
   696  	} else if !exists {
   697  		return "", creds.ErrCredsNotFound
   698  	} else {
   699  		return path, nil
   700  	}
   701  }
   702  
   703  func (dEnv *DoltEnv) FindRef(ctx context.Context, refStr string) (ref.DoltRef, error) {
   704  	localRef := ref.NewBranchRef(refStr)
   705  	if hasRef, err := dEnv.DoltDB.HasRef(ctx, localRef); err != nil {
   706  		return nil, err
   707  	} else if hasRef {
   708  		return localRef, nil
   709  	} else {
   710  		if strings.HasPrefix(refStr, "remotes/") {
   711  			refStr = refStr[len("remotes/"):]
   712  		}
   713  
   714  		slashIdx := strings.IndexRune(refStr, '/')
   715  		if slashIdx > 0 {
   716  			remoteName := refStr[:slashIdx]
   717  			if _, ok := dEnv.RepoState.Remotes[remoteName]; ok {
   718  				remoteRef, err := ref.NewRemoteRefFromPathStr(refStr)
   719  
   720  				if err != nil {
   721  					return nil, err
   722  				}
   723  
   724  				if hasRef, err = dEnv.DoltDB.HasRef(ctx, remoteRef); err != nil {
   725  					return nil, err
   726  				} else if hasRef {
   727  					return remoteRef, nil
   728  				}
   729  			}
   730  		}
   731  	}
   732  
   733  	return nil, doltdb.ErrBranchNotFound
   734  }
   735  
   736  // GetRefSpecs takes an optional remoteName and returns all refspecs associated with that remote.  If "" is passed as
   737  // the remoteName then the default remote is used.
   738  func (dEnv *DoltEnv) GetRefSpecs(remoteName string) ([]ref.RemoteRefSpec, errhand.VerboseError) {
   739  	var remote Remote
   740  	var verr errhand.VerboseError
   741  
   742  	if remoteName == "" {
   743  		remote, verr = dEnv.GetDefaultRemote()
   744  	} else if r, ok := dEnv.RepoState.Remotes[remoteName]; ok {
   745  		remote = r
   746  	} else {
   747  		verr = errhand.BuildDError("error: unknown remote '%s'", remoteName).Build()
   748  	}
   749  
   750  	if verr != nil {
   751  		return nil, verr
   752  	}
   753  
   754  	var refSpecs []ref.RemoteRefSpec
   755  	for _, fs := range remote.FetchSpecs {
   756  		rs, err := ref.ParseRefSpecForRemote(remote.Name, fs)
   757  
   758  		if err != nil {
   759  			return nil, errhand.BuildDError("error: for '%s', '%s' is not a valid refspec.", remote.Name, fs).Build()
   760  		}
   761  
   762  		if rrs, ok := rs.(ref.RemoteRefSpec); !ok {
   763  			return nil, errhand.BuildDError("error: '%s' is not a valid refspec referring to a remote tracking branch", remote.Name).Build()
   764  		} else if rrs.GetRemote() != remote.Name {
   765  			return nil, errhand.BuildDError("error: remote '%s' refers to remote '%s'", remote.Name, rrs.GetRemote()).Build()
   766  		} else {
   767  			refSpecs = append(refSpecs, rrs)
   768  		}
   769  	}
   770  
   771  	return refSpecs, nil
   772  }
   773  
   774  var ErrNoRemote = errhand.BuildDError("error: no remote.").Build()
   775  var ErrCantDetermineDefault = errhand.BuildDError("error: unable to determine the default remote.").Build()
   776  
   777  // GetDefaultRemote gets the default remote for the environment.  Not fully implemented yet.  Needs to support multiple
   778  // repos and a configurable default.
   779  func (dEnv *DoltEnv) GetDefaultRemote() (Remote, errhand.VerboseError) {
   780  	remotes := dEnv.RepoState.Remotes
   781  
   782  	if len(remotes) == 0 {
   783  		return NoRemote, ErrNoRemote
   784  	} else if len(remotes) == 1 {
   785  		for _, v := range remotes {
   786  			return v, nil
   787  		}
   788  	}
   789  
   790  	if remote, ok := dEnv.RepoState.Remotes["origin"]; ok {
   791  		return remote, nil
   792  	}
   793  
   794  	return NoRemote, ErrCantDetermineDefault
   795  }
   796  
   797  // GetUserHomeDir returns the user's home dir
   798  // based on current filesys
   799  func (dEnv *DoltEnv) GetUserHomeDir() (string, error) {
   800  	return getHomeDir(dEnv.hdp)
   801  }
   802  
   803  func (dEnv *DoltEnv) TempTableFilesDir() string {
   804  	return mustAbs(dEnv, dEnv.GetDoltDir(), tempTablesDir)
   805  }