git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/client/client.go (about) 1 package client 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "crypto/tls" 7 "errors" 8 "time" 9 10 v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" 11 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" 12 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" 13 "google.golang.org/grpc" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/grpc/status" 16 ) 17 18 // Client represents virtual connection to the FrostFS network to communicate 19 // with FrostFS server using FrostFS API protocol. It is designed to provide 20 // an abstraction interface from the protocol details of data transfer over 21 // a network in FrostFS. 22 // 23 // Client can be created using simple Go variable declaration. Before starting 24 // work with the Client, it SHOULD BE correctly initialized (see Init method). 25 // Before executing the FrostFS operations using the Client, connection to the 26 // server MUST BE correctly established (see Dial method and pay attention 27 // to the mandatory parameters). Using the Client before connecting have 28 // been established can lead to a panic. After the work, the Client SHOULD BE 29 // closed (see Close method): it frees internal and system resources which were 30 // allocated for the period of work of the Client. Calling Init/Dial/Close method 31 // during the communication process step strongly discouraged as it leads to 32 // undefined behavior. 33 // 34 // Each method which produces a FrostFS API call may return a server response. 35 // Status responses are returned in the result structure, and can be cast 36 // to built-in error instance (or in the returned error if the client is 37 // configured accordingly). Certain statuses can be checked using `apistatus` 38 // and standard `errors` packages. Note that package provides some helper 39 // functions to work with status returns (e.g. IsErrContainerNotFound). 40 // All possible responses are documented in methods, however, some may be 41 // returned from all of them (pay attention to the presence of the pointer sign): 42 // - *apistatus.ServerInternal on internal server error; 43 // - *apistatus.NodeUnderMaintenance if a server is under maintenance; 44 // - *apistatus.SuccessDefaultV2 on default success. 45 // 46 // Client MUST NOT be copied by value: use pointer to Client instead. 47 // 48 // See client package overview to get some examples. 49 type Client struct { 50 prm PrmInit 51 52 c client.Client 53 54 server frostFSAPIServer 55 } 56 57 // Init brings the Client instance to its initial state. 58 // 59 // One-time method call during application init stage (before Dial) is expected. 60 // Calling multiple times leads to undefined behavior. 61 // 62 // See docs of PrmInit methods for details. See also Dial / Close. 63 func (c *Client) Init(prm PrmInit) { 64 c.prm = prm 65 } 66 67 // Dial establishes a connection to the server from the FrostFS network. 68 // Returns an error describing failure reason. If failed, the Client 69 // SHOULD NOT be used. 70 // 71 // Uses the context specified by SetContext if it was called with non-nil 72 // argument, otherwise context.Background() is used. Dial returns context 73 // errors, see context package docs for details. 74 // 75 // Returns an error if required parameters are set incorrectly, look carefully 76 // at the method documentation. 77 // 78 // One-time method call during application start-up stage (after Init ) is expected. 79 // Calling multiple times leads to undefined behavior. 80 // 81 // See also Init / Close. 82 func (c *Client) Dial(ctx context.Context, prm PrmDial) error { 83 if prm.Endpoint == "" { 84 return errorServerAddrUnset 85 } 86 87 if prm.DialTimeout <= 0 { 88 prm.DialTimeout = defaultDialTimeout 89 } 90 if prm.StreamTimeout <= 0 { 91 prm.StreamTimeout = defaultStreamTimeout 92 } 93 94 c.c = *client.New(append( 95 client.WithNetworkURIAddress(prm.Endpoint, prm.TLSConfig), 96 client.WithDialTimeout(prm.DialTimeout), 97 client.WithRWTimeout(prm.StreamTimeout), 98 client.WithGRPCDialOptions(prm.GRPCDialOptions), 99 )...) 100 101 c.setFrostFSAPIServer((*coreServer)(&c.c)) 102 103 ctx, cancel := context.WithTimeout(ctx, prm.DialTimeout) 104 defer cancel() 105 _, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest), 106 client.WithContext(ctx), 107 ) 108 if err != nil { 109 var ctxErr error 110 111 // return context errors since they signal about dial problem 112 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { 113 ctxErr = err 114 } else if st, ok := status.FromError(err); ok && st.Code() == codes.Canceled { 115 ctxErr = context.Canceled 116 } else if ok && st.Code() == codes.DeadlineExceeded { 117 ctxErr = context.DeadlineExceeded 118 } 119 if ctxErr != nil { 120 if conn := c.c.Conn(); conn != nil { 121 _ = conn.Close() 122 } 123 return ctxErr 124 } 125 } 126 127 return nil 128 } 129 130 // sets underlying provider of frostFSAPIServer. The method is used for testing as an approach 131 // to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code. 132 // In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client 133 // is statically used. 134 func (c *Client) setFrostFSAPIServer(server frostFSAPIServer) { 135 c.server = server 136 } 137 138 // Close closes underlying connection to the FrostFS server. Implements io.Closer. 139 // MUST NOT be called before successful Dial. Can be called concurrently 140 // with server operations processing on running goroutines: in this case 141 // they are likely to fail due to a connection error. 142 // 143 // One-time method call during application shutdown stage (after Init and Dial) 144 // is expected. Calling multiple times leads to undefined behavior. 145 // 146 // See also Init / Dial. 147 func (c *Client) Close() error { 148 return c.c.Conn().Close() 149 } 150 151 // PrmInit groups initialization parameters of Client instances. 152 // 153 // See also Init. 154 type PrmInit struct { 155 DisableFrostFSErrorResolution bool 156 157 Key ecdsa.PrivateKey 158 159 ResponseInfoCallback func(ResponseMetaInfo) error 160 161 NetMagic uint64 162 } 163 164 // SetDefaultPrivateKey sets Client private key to be used for the protocol 165 // communication by default. 166 // 167 // Required for operations without custom key parametrization (see corresponding Prm* docs). 168 // 169 // Deprecated: Use PrmInit.Key instead. 170 func (x *PrmInit) SetDefaultPrivateKey(key ecdsa.PrivateKey) { 171 x.Key = key 172 } 173 174 // Deprecated: method is no-op. Option is default. 175 func (x *PrmInit) ResolveFrostFSFailures() { 176 } 177 178 // DisableFrostFSFailuresResolution makes the Client to preserve failure statuses of the 179 // FrostFS protocol only in resulting structure (see corresponding Res* docs). 180 // These errors are returned from each protocol operation. By default, statuses 181 // are resolved and returned as a Go built-in errors. 182 // 183 // Deprecated: Use PrmInit.DisableFrostFSErrorResolution instead. 184 func (x *PrmInit) DisableFrostFSFailuresResolution() { 185 x.DisableFrostFSErrorResolution = true 186 } 187 188 // SetResponseInfoCallback makes the Client to pass ResponseMetaInfo from each 189 // FrostFS server response to f. Nil (default) means ignore response meta info. 190 // 191 // Deprecated: Use PrmInit.ResponseInfoCallback instead. 192 func (x *PrmInit) SetResponseInfoCallback(f func(ResponseMetaInfo) error) { 193 x.ResponseInfoCallback = f 194 } 195 196 const ( 197 defaultDialTimeout = 5 * time.Second 198 defaultStreamTimeout = 10 * time.Second 199 ) 200 201 // PrmDial groups connection parameters for the Client. 202 // 203 // See also Dial. 204 type PrmDial struct { 205 Endpoint string 206 207 TLSConfig *tls.Config 208 209 // If DialTimeout is non-positive, then it's set to defaultDialTimeout. 210 DialTimeout time.Duration 211 212 // If StreamTimeout is non-positive, then it's set to defaultStreamTimeout. 213 StreamTimeout time.Duration 214 215 GRPCDialOptions []grpc.DialOption 216 } 217 218 // SetServerURI sets server URI in the FrostFS network. 219 // Required parameter. 220 // 221 // Format of the URI: 222 // 223 // [scheme://]host:port 224 // 225 // Supported schemes: 226 // 227 // grpc 228 // grpcs 229 // 230 // See also SetTLSConfig. 231 // 232 // Deprecated: Use PrmDial.Endpoint instead. 233 func (x *PrmDial) SetServerURI(endpoint string) { 234 x.Endpoint = endpoint 235 } 236 237 // SetTLSConfig sets tls.Config to open TLS client connection 238 // to the FrostFS server. Nil (default) means insecure connection. 239 // 240 // See also SetServerURI. 241 // 242 // Depreacted: Use PrmDial.TLSConfig instead. 243 func (x *PrmDial) SetTLSConfig(tlsConfig *tls.Config) { 244 x.TLSConfig = tlsConfig 245 } 246 247 // SetTimeout sets the timeout for connection to be established. 248 // MUST BE positive. If not called, 5s timeout will be used by default. 249 // 250 // Deprecated: Use PrmDial.DialTimeout instead. 251 func (x *PrmDial) SetTimeout(timeout time.Duration) { 252 x.DialTimeout = timeout 253 } 254 255 // SetStreamTimeout sets the timeout for individual operations in streaming RPC. 256 // MUST BE positive. If not called, 10s timeout will be used by default. 257 // 258 // Deprecated: Use PrmDial.StreamTimeout instead. 259 func (x *PrmDial) SetStreamTimeout(timeout time.Duration) { 260 x.StreamTimeout = timeout 261 } 262 263 // SetGRPCDialOptions sets the gRPC dial options for new gRPC client connection. 264 // 265 // Deprecated: Use PrmDial.GRPCDialOptions instead. 266 func (x *PrmDial) SetGRPCDialOptions(opts ...grpc.DialOption) { 267 x.GRPCDialOptions = opts 268 }