github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/dbfactory/grpc.go (about) 1 // Copyright 2019 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 dbfactory 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net/url" 22 23 "google.golang.org/grpc" 24 25 remotesapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/remotesapi/v1alpha1" 26 "github.com/dolthub/dolt/go/libraries/doltcore/grpcendpoint" 27 "github.com/dolthub/dolt/go/libraries/doltcore/remotestorage" 28 "github.com/dolthub/dolt/go/libraries/events" 29 "github.com/dolthub/dolt/go/store/chunks" 30 "github.com/dolthub/dolt/go/store/datas" 31 "github.com/dolthub/dolt/go/store/prolly/tree" 32 "github.com/dolthub/dolt/go/store/types" 33 ) 34 35 var GRPCDialProviderParam = "__DOLT__grpc_dial_provider" 36 var GRPCUsernameAuthParam = "__DOLT__grpc_username" 37 38 type GRPCRemoteConfig struct { 39 Endpoint string 40 DialOptions []grpc.DialOption 41 HTTPFetcher grpcendpoint.HTTPFetcher 42 } 43 44 // GRPCDialProvider is an interface for getting a concrete Endpoint, 45 // DialOptions and HTTPFetcher from a slightly more abstract 46 // grpcendpoint.Config. It allows a caller to override certain aspects of how 47 // the grpc.ClientConn and the resulting remotestorage ChunkStore are 48 // configured by dbfactory when it returns remotestorage DBs. 49 // 50 // An instance of this must be provided in |params[GRPCDialProviderParam]| when 51 // calling |CreateDB| with a remotesapi remote. See *env.Remote for example. 52 type GRPCDialProvider interface { 53 GetGRPCDialParams(grpcendpoint.Config) (GRPCRemoteConfig, error) 54 } 55 56 // DoldRemoteFactory is a DBFactory implementation for creating databases backed by a remote server that implements the 57 // GRPC rpcs defined by remoteapis.ChunkStoreServiceClient 58 type DoltRemoteFactory struct { 59 insecure bool 60 } 61 62 func (fact DoltRemoteFactory) PrepareDB(ctx context.Context, nbf *types.NomsBinFormat, urlObj *url.URL, params map[string]interface{}) error { 63 return fmt.Errorf("http(s) scheme cannot support this operation") 64 } 65 66 // NewDoltRemoteFactory creates a DoltRemoteFactory instance using the given GRPCConnectionProvider, and insecure setting 67 func NewDoltRemoteFactory(insecure bool) DoltRemoteFactory { 68 return DoltRemoteFactory{insecure} 69 } 70 71 // CreateDB creates a database backed by a remote server that implements the GRPC rpcs defined by 72 // remoteapis.ChunkStoreServiceClient 73 func (fact DoltRemoteFactory) CreateDB(ctx context.Context, nbf *types.NomsBinFormat, urlObj *url.URL, params map[string]interface{}) (datas.Database, types.ValueReadWriter, tree.NodeStore, error) { 74 var db datas.Database 75 76 dpi, ok := params[GRPCDialProviderParam] 77 if dpi == nil || !ok { 78 return nil, nil, nil, errors.New("DoltRemoteFactory.CreateDB must provide a GRPCDialProvider param through GRPCDialProviderParam") 79 } 80 dp, ok := dpi.(GRPCDialProvider) 81 if !ok { 82 return nil, nil, nil, errors.New("DoltRemoteFactory.CreateDB must provide a GRPCDialProvider param through GRPCDialProviderParam") 83 } 84 85 cs, err := fact.newChunkStore(ctx, nbf, urlObj, params, dp) 86 87 if err != nil { 88 return nil, nil, nil, err 89 } 90 91 vrw := types.NewValueStore(cs) 92 ns := tree.NewNodeStore(cs) 93 db = datas.NewTypesDatabase(vrw, ns) 94 95 return db, vrw, ns, err 96 } 97 98 // If |params[NoCachingParameter]| is set in |params| of the CreateDB call for 99 // a remotesapi database, then the configured database will have caching at the 100 // remotestorage.ChunkStore layer disabled. 101 var NoCachingParameter = "__dolt__NO_CACHING" 102 103 func (fact DoltRemoteFactory) newChunkStore(ctx context.Context, nbf *types.NomsBinFormat, urlObj *url.URL, params map[string]interface{}, dp GRPCDialProvider) (chunks.ChunkStore, error) { 104 var user string 105 wsValidate := false 106 if userParam := params[GRPCUsernameAuthParam]; userParam != nil { 107 user = userParam.(string) 108 wsValidate = true 109 } 110 cfg, err := dp.GetGRPCDialParams(grpcendpoint.Config{ 111 Endpoint: urlObj.Host, 112 Insecure: fact.insecure, 113 UserIdForOsEnvAuth: user, 114 WithEnvCreds: true, 115 }) 116 if err != nil { 117 return nil, err 118 } 119 120 opts := append(cfg.DialOptions, grpc.WithChainUnaryInterceptor(remotestorage.EventsUnaryClientInterceptor(events.GlobalCollector()))) 121 opts = append(opts, grpc.WithChainUnaryInterceptor(remotestorage.RetryingUnaryClientInterceptor)) 122 123 conn, err := grpc.Dial(cfg.Endpoint, opts...) 124 if err != nil { 125 return nil, err 126 } 127 128 csClient := remotesapi.NewChunkStoreServiceClient(conn) 129 cs, err := remotestorage.NewDoltChunkStoreFromPath(ctx, nbf, urlObj.Path, urlObj.Host, wsValidate, csClient) 130 if err != nil { 131 conn.Close() 132 return nil, fmt.Errorf("could not access dolt url '%s': %w", urlObj.String(), err) 133 } 134 cs = cs.WithHTTPFetcher(cfg.HTTPFetcher) 135 cs.SetFinalizer(conn.Close) 136 137 if _, ok := params[NoCachingParameter]; ok { 138 cs = cs.WithNoopChunkCache() 139 } 140 141 return cs, nil 142 }