2021-07-21 17:24:34 +08:00
package shell
import (
"context"
"flag"
"fmt"
2021-08-08 16:21:42 +08:00
"github.com/golang/protobuf/jsonpb"
2021-07-21 17:24:34 +08:00
"github.com/golang/protobuf/proto"
2022-07-29 15:17:28 +08:00
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/pb/remote_pb"
"github.com/seaweedfs/seaweedfs/weed/remote_storage"
"github.com/seaweedfs/seaweedfs/weed/util"
2021-07-21 17:24:34 +08:00
"io"
"regexp"
2021-07-27 16:16:28 +08:00
"strings"
2021-07-21 17:24:34 +08:00
)
func init ( ) {
Commands = append ( Commands , & commandRemoteConfigure { } )
}
type commandRemoteConfigure struct {
}
func ( c * commandRemoteConfigure ) Name ( ) string {
return "remote.configure"
}
func ( c * commandRemoteConfigure ) Help ( ) string {
return ` remote storage configuration
# see the current configurations
remote . configure
# set or update a configuration
2021-08-31 17:04:32 +08:00
remote . configure - name = cloud1 - type = s3 - s3 . access_key = xxx - s3 . secret_key = yyy - s3 . region = us - east - 2
2021-09-04 14:38:33 +08:00
remote . configure - name = cloud2 - type = gcs - gcs . appCredentialsFile = ~ / service - account - file . json - gcs . projectId = yyy
2021-08-24 16:18:30 +08:00
remote . configure - name = cloud3 - type = azure - azure . account_name = xxx - azure . account_key = yyy
2021-08-28 16:09:48 +08:00
remote . configure - name = cloud4 - type = aliyun - aliyun . access_key = xxx - aliyun . secret_key = yyy - aliyun . endpoint = oss - cn - shenzhen . aliyuncs . com - aliyun . region = cn - sehnzhen
2021-08-28 17:23:03 +08:00
remote . configure - name = cloud5 - type = tencent - tencent . secret_id = xxx - tencent . secret_key = yyy - tencent . endpoint = cos . ap - guangzhou . myqcloud . com
2021-08-28 20:56:31 +08:00
remote . configure - name = cloud6 - type = wasabi - wasabi . access_key = xxx - wasabi . secret_key = yyy - wasabi . endpoint = s3 . us - west - 1. wasabisys . com - wasabi . region = us - west - 1
2021-08-31 08:43:14 +08:00
remote . configure - name = cloud7 - type = storj - storj . access_key = xxx - storj . secret_key = yyy - storj . endpoint = https : //gateway.us1.storjshare.io
remote . configure - name = cloud8 - type = filebase - filebase . access_key = xxx - filebase . secret_key = yyy - filebase . endpoint = https : //s3.filebase.com
2021-07-21 17:24:34 +08:00
# delete one configuration
remote . configure - delete - name = cloud1
`
}
var (
isAlpha = regexp . MustCompile ( ` ^[A-Za-z][A-Za-z0-9]*$ ` ) . MatchString
)
func ( c * commandRemoteConfigure ) Do ( args [ ] string , commandEnv * CommandEnv , writer io . Writer ) ( err error ) {
2021-08-27 06:18:34 +08:00
conf := & remote_pb . RemoteConf { }
2021-07-21 17:24:34 +08:00
remoteConfigureCommand := flag . NewFlagSet ( c . Name ( ) , flag . ContinueOnError )
isDelete := remoteConfigureCommand . Bool ( "delete" , false , "delete one remote storage by its name" )
remoteConfigureCommand . StringVar ( & conf . Name , "name" , "" , "a short name to identify the remote storage" )
2021-09-04 15:18:21 +08:00
remoteConfigureCommand . StringVar ( & conf . Type , "type" , "s3" , fmt . Sprintf ( "[%s] storage type" , remote_storage . GetAllRemoteStorageNames ( ) ) )
2021-07-21 17:24:34 +08:00
remoteConfigureCommand . StringVar ( & conf . S3AccessKey , "s3.access_key" , "" , "s3 access key" )
remoteConfigureCommand . StringVar ( & conf . S3SecretKey , "s3.secret_key" , "" , "s3 secret key" )
remoteConfigureCommand . StringVar ( & conf . S3Region , "s3.region" , "us-east-2" , "s3 region" )
remoteConfigureCommand . StringVar ( & conf . S3Endpoint , "s3.endpoint" , "" , "endpoint for s3-compatible local object store" )
2021-08-23 17:18:56 +08:00
remoteConfigureCommand . StringVar ( & conf . S3StorageClass , "s3.storage_class" , "" , "s3 storage class" )
2021-08-23 18:30:41 +08:00
remoteConfigureCommand . BoolVar ( & conf . S3ForcePathStyle , "s3.force_path_style" , true , "s3 force path style" )
2021-08-31 08:28:33 +08:00
remoteConfigureCommand . BoolVar ( & conf . S3V4Signature , "s3.v4_signature" , false , "s3 V4 signature" )
2021-07-21 17:24:34 +08:00
2021-08-24 16:18:30 +08:00
remoteConfigureCommand . StringVar ( & conf . GcsGoogleApplicationCredentials , "gcs.appCredentialsFile" , "" , "google cloud storage credentials file, default to use env GOOGLE_APPLICATION_CREDENTIALS" )
2021-09-04 14:38:33 +08:00
remoteConfigureCommand . StringVar ( & conf . GcsProjectId , "gcs.projectId" , "" , "google cloud storage project id, default to use env GOOGLE_CLOUD_PROJECT" )
2021-08-24 16:18:30 +08:00
remoteConfigureCommand . StringVar ( & conf . AzureAccountName , "azure.account_name" , "" , "azure account name, default to use env AZURE_STORAGE_ACCOUNT" )
remoteConfigureCommand . StringVar ( & conf . AzureAccountKey , "azure.account_key" , "" , "azure account name, default to use env AZURE_STORAGE_ACCESS_KEY" )
2021-08-23 15:29:27 +08:00
2021-08-25 13:30:06 +08:00
remoteConfigureCommand . StringVar ( & conf . BackblazeKeyId , "b2.key_id" , "" , "backblaze keyID" )
remoteConfigureCommand . StringVar ( & conf . BackblazeApplicationKey , "b2.application_key" , "" , "backblaze applicationKey. Note that your Master Application Key will not work with the S3 Compatible API. You must create a new key that is eligible for use. For more information: https://help.backblaze.com/hc/en-us/articles/360047425453" )
remoteConfigureCommand . StringVar ( & conf . BackblazeEndpoint , "b2.endpoint" , "" , "backblaze endpoint" )
2021-08-25 14:25:36 +08:00
remoteConfigureCommand . StringVar ( & conf . AliyunAccessKey , "aliyun.access_key" , "" , "Aliyun access key, default to use env ALICLOUD_ACCESS_KEY_ID" )
remoteConfigureCommand . StringVar ( & conf . AliyunSecretKey , "aliyun.secret_key" , "" , "Aliyun secret key, default to use env ALICLOUD_ACCESS_KEY_SECRET" )
2021-08-25 14:14:24 +08:00
remoteConfigureCommand . StringVar ( & conf . AliyunEndpoint , "aliyun.endpoint" , "" , "Aliyun endpoint" )
2021-08-25 14:46:33 +08:00
remoteConfigureCommand . StringVar ( & conf . AliyunRegion , "aliyun.region" , "" , "Aliyun region" )
2021-08-25 14:14:24 +08:00
2021-08-25 14:25:36 +08:00
remoteConfigureCommand . StringVar ( & conf . TencentSecretId , "tencent.secret_id" , "" , "Tencent Secret Id, default to use env COS_SECRETID" )
remoteConfigureCommand . StringVar ( & conf . TencentSecretKey , "tencent.secret_key" , "" , "Tencent secret key, default to use env COS_SECRETKEY" )
2021-08-25 14:19:45 +08:00
remoteConfigureCommand . StringVar ( & conf . TencentEndpoint , "tencent.endpoint" , "" , "Tencent endpoint" )
2021-08-25 14:46:33 +08:00
remoteConfigureCommand . StringVar ( & conf . BaiduAccessKey , "baidu.access_key" , "" , "Baidu access key, default to use env BDCLOUD_ACCESS_KEY" )
remoteConfigureCommand . StringVar ( & conf . BaiduSecretKey , "baidu.secret_key" , "" , "Baidu secret key, default to use env BDCLOUD_SECRET_KEY" )
remoteConfigureCommand . StringVar ( & conf . BaiduEndpoint , "baidu.endpoint" , "" , "Baidu endpoint" )
remoteConfigureCommand . StringVar ( & conf . BaiduRegion , "baidu.region" , "" , "Baidu region" )
2021-08-26 08:34:29 +08:00
remoteConfigureCommand . StringVar ( & conf . WasabiAccessKey , "wasabi.access_key" , "" , "Wasabi access key" )
remoteConfigureCommand . StringVar ( & conf . WasabiSecretKey , "wasabi.secret_key" , "" , "Wasabi secret key" )
remoteConfigureCommand . StringVar ( & conf . WasabiEndpoint , "wasabi.endpoint" , "" , "Wasabi endpoint, see https://wasabi.com/wp-content/themes/wasabi/docs/API_Guide/index.html#t=topics%2Fapidiff-intro.htm" )
remoteConfigureCommand . StringVar ( & conf . WasabiRegion , "wasabi.region" , "" , "Wasabi region" )
2021-08-31 08:43:14 +08:00
remoteConfigureCommand . StringVar ( & conf . FilebaseAccessKey , "filebase.access_key" , "" , "Filebase access key" )
remoteConfigureCommand . StringVar ( & conf . FilebaseSecretKey , "filebase.secret_key" , "" , "Filebase secret key" )
remoteConfigureCommand . StringVar ( & conf . FilebaseEndpoint , "filebase.endpoint" , "" , "Filebase endpoint, https://s3.filebase.com" )
remoteConfigureCommand . StringVar ( & conf . StorjAccessKey , "storj.access_key" , "" , "Storj access key" )
remoteConfigureCommand . StringVar ( & conf . StorjSecretKey , "storj.secret_key" , "" , "Storj secret key" )
remoteConfigureCommand . StringVar ( & conf . StorjEndpoint , "storj.endpoint" , "" , "Storj endpoint" )
2021-07-21 17:24:34 +08:00
if err = remoteConfigureCommand . Parse ( args ) ; err != nil {
return nil
}
2021-08-30 09:41:29 +08:00
if conf . Type != "s3" {
// clear out the default values
conf . S3Region = ""
conf . S3ForcePathStyle = false
}
2021-07-21 17:24:34 +08:00
if conf . Name == "" {
return c . listExistingRemoteStorages ( commandEnv , writer )
}
if ! isAlpha ( conf . Name ) {
return fmt . Errorf ( "only letters and numbers allowed in name: %v" , conf . Name )
}
if * isDelete {
return c . deleteRemoteStorage ( commandEnv , writer , conf . Name )
}
return c . saveRemoteStorage ( commandEnv , writer , conf )
}
func ( c * commandRemoteConfigure ) listExistingRemoteStorages ( commandEnv * CommandEnv , writer io . Writer ) error {
return filer_pb . ReadDirAllEntries ( commandEnv , util . FullPath ( filer . DirectoryEtcRemote ) , "" , func ( entry * filer_pb . Entry , isLast bool ) error {
if len ( entry . Content ) == 0 {
fmt . Fprintf ( writer , "skipping %s\n" , entry . Name )
return nil
}
2021-07-27 16:16:28 +08:00
if ! strings . HasSuffix ( entry . Name , filer . REMOTE_STORAGE_CONF_SUFFIX ) {
return nil
}
2021-08-27 06:18:34 +08:00
conf := & remote_pb . RemoteConf { }
2021-07-21 17:24:34 +08:00
if err := proto . Unmarshal ( entry . Content , conf ) ; err != nil {
return fmt . Errorf ( "unmarshal %s/%s: %v" , filer . DirectoryEtcRemote , entry . Name , err )
}
2021-09-06 05:23:49 +08:00
// change secret key to stars
2021-08-18 02:27:08 +08:00
conf . S3SecretKey = strings . Repeat ( "*" , len ( conf . S3SecretKey ) )
2021-09-06 05:23:49 +08:00
conf . AliyunSecretKey = strings . Repeat ( "*" , len ( conf . AliyunSecretKey ) )
conf . BaiduAccessKey = strings . Repeat ( "*" , len ( conf . BaiduAccessKey ) )
conf . FilebaseSecretKey = strings . Repeat ( "*" , len ( conf . FilebaseSecretKey ) )
conf . StorjSecretKey = strings . Repeat ( "*" , len ( conf . StorjSecretKey ) )
conf . TencentSecretKey = strings . Repeat ( "*" , len ( conf . TencentSecretKey ) )
conf . WasabiSecretKey = strings . Repeat ( "*" , len ( conf . WasabiSecretKey ) )
2021-07-21 17:24:34 +08:00
2021-08-08 16:21:42 +08:00
m := jsonpb . Marshaler {
EmitDefaults : false ,
Indent : " " ,
}
2021-07-21 17:24:34 +08:00
2021-08-08 16:21:42 +08:00
err := m . Marshal ( writer , conf )
fmt . Fprintln ( writer )
return err
2021-07-21 17:24:34 +08:00
} )
}
func ( c * commandRemoteConfigure ) deleteRemoteStorage ( commandEnv * CommandEnv , writer io . Writer , storageName string ) error {
2021-12-26 16:15:03 +08:00
return commandEnv . WithFilerClient ( false , func ( client filer_pb . SeaweedFilerClient ) error {
2021-07-21 17:24:34 +08:00
request := & filer_pb . DeleteEntryRequest {
Directory : filer . DirectoryEtcRemote ,
2021-07-27 16:16:28 +08:00
Name : storageName + filer . REMOTE_STORAGE_CONF_SUFFIX ,
2021-07-21 17:24:34 +08:00
IgnoreRecursiveError : false ,
IsDeleteData : true ,
IsRecursive : true ,
IsFromOtherCluster : false ,
Signatures : nil ,
}
_ , err := client . DeleteEntry ( context . Background ( ) , request )
if err == nil {
fmt . Fprintf ( writer , "removed: %s\n" , storageName )
}
return err
} )
}
2021-08-27 06:18:34 +08:00
func ( c * commandRemoteConfigure ) saveRemoteStorage ( commandEnv * CommandEnv , writer io . Writer , conf * remote_pb . RemoteConf ) error {
2021-07-21 17:24:34 +08:00
data , err := proto . Marshal ( conf )
if err != nil {
return err
}
2021-12-26 16:15:03 +08:00
if err = commandEnv . WithFilerClient ( false , func ( client filer_pb . SeaweedFilerClient ) error {
2021-07-27 16:16:28 +08:00
return filer . SaveInsideFiler ( client , filer . DirectoryEtcRemote , conf . Name + filer . REMOTE_STORAGE_CONF_SUFFIX , data )
2021-07-21 17:24:34 +08:00
} ) ; err != nil && err != filer_pb . ErrNotFound {
return err
}
return nil
}