storj/cmd/uplink/ulext/external.go
Kaloyan Raev 978e0f1a26 cmd/uplink: cp sets connection pool capacity based on parallelism
If the cp command is executed with higher level of parallelism, it would
open more connections to storage nodes at the same time. Therefore, the
connection pool capacity should be expanded accordingly.

The pool capacity is set to 100 * parallelism.

Change-Id: Ia8b3ab6a99340d8cbb87a7b80c3354b2b21c1958
2022-04-21 14:10:08 +00:00

126 lines
3.8 KiB
Go

// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
// Package ulext provides an interface for the CLI to interface with the external world.
package ulext
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/zeebo/clingy"
"github.com/zeebo/errs"
"storj.io/common/rpc/rpcpool"
"storj.io/storj/cmd/uplink/ulfs"
"storj.io/uplink"
)
// External is the interface for all of the ways that the uplink command may interact with
// any external state.
type External interface {
OpenFilesystem(ctx context.Context, accessName string, options ...Option) (ulfs.Filesystem, error)
OpenProject(ctx context.Context, accessName string, options ...Option) (*uplink.Project, error)
AccessInfoFile() string
OpenAccess(accessName string) (access *uplink.Access, err error)
GetAccessInfo(required bool) (string, map[string]string, error)
SaveAccessInfo(defaultName string, accesses map[string]string) error
RequestAccess(ctx context.Context, satelliteAddress, apiKey, passphrase string) (*uplink.Access, error)
ExportAccess(ctx clingy.Context, access *uplink.Access, filename string) error
ConfigFile() string
SaveConfig(values map[string]string) error
PromptInput(ctx clingy.Context, prompt string) (input string, err error)
PromptSecret(ctx clingy.Context, prompt string) (secret string, err error)
}
// Options contains all of the possible options for opening a filesystem or project.
type Options struct {
EncryptionBypass bool
ConnectionPoolOptions rpcpool.Options
}
// LoadOptions takes a slice of Option values and returns a filled out Options struct.
func LoadOptions(options ...Option) (opts Options) {
for _, opt := range options {
opt.apply(&opts)
}
return opts
}
// Option is a single option that controls the Options struct.
type Option struct {
apply func(*Options)
}
// BypassEncryption will disable decrypting of path names if bypass is true.
func BypassEncryption(bypass bool) Option {
return Option{apply: func(opt *Options) { opt.EncryptionBypass = bypass }}
}
// ConnectionPoolOptions will initialize the connection pool with options.
func ConnectionPoolOptions(options rpcpool.Options) Option {
return Option{apply: func(opt *Options) { opt.ConnectionPoolOptions = options }}
}
// RegisterAccess registers an access grant with a Gateway Authorization Service.
func RegisterAccess(ctx context.Context, access *uplink.Access, authService string, public bool, timeout time.Duration) (accessKey, secretKey, endpoint string, err error) {
if authService == "" {
return "", "", "", errs.New("no auth service address provided")
}
accessSerialized, err := access.Serialize()
if err != nil {
return "", "", "", errs.Wrap(err)
}
postData, err := json.Marshal(map[string]interface{}{
"access_grant": accessSerialized,
"public": public,
})
if err != nil {
return accessKey, "", "", errs.Wrap(err)
}
client := &http.Client{
Timeout: timeout,
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/v1/access", authService), bytes.NewReader(postData))
if err != nil {
return "", "", "", err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", "", "", err
}
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", "", err
}
respBody := make(map[string]string)
if err := json.Unmarshal(body, &respBody); err != nil {
return "", "", "", errs.New("unexpected response from auth service: %s", string(body))
}
accessKey, ok := respBody["access_key_id"]
if !ok {
return "", "", "", errs.New("access_key_id missing in response")
}
secretKey, ok = respBody["secret_key"]
if !ok {
return "", "", "", errs.New("secret_key missing in response")
}
return accessKey, secretKey, respBody["endpoint"], nil
}