package userspace import ( "fmt" "strconv" "strings" "github.com/chzyer/readline" "github.com/spf13/cobra" "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1" cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types" "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd" ) type MyUserSpace struct { api.UserSpaceCreate } func init() { cmd := cobra.Command{ Use: "create", Short: "add a new cloud storage", Run: func(c *cobra.Command, args []string) { ctx := cmd.GetCmdCtx(c) create(c, ctx) }, } UserSpaceCmd.AddCommand(&cmd) } func create(c *cobra.Command, ctx *cmd.CommandContext) { var userSpace MyUserSpace rl, err := readline.New("> ") if err != nil { fmt.Printf("初始化命令行失败: %v\n", err) return } defer rl.Close() rl.SetPrompt("\033[36m请输入存储服务名称(Name): \033[0m") name, err := rl.Readline() if err != nil { return } userSpace.Name = name storageType, err := promptSelectStorage() if err != nil { return } switch storageType { case "Local": err = userSpace.collectLocalConfig(rl) case "OBS": err = userSpace.collectObsConfig(rl) case "OSS": err = userSpace.collectOssConfig(rl) case "COS": err = userSpace.collectCosConfig(rl) case "EFile": err = userSpace.collectEfileConfig(rl) case "S3": err = userSpace.collectS3Config(rl) } if err != nil { return } err = userSpace.collectShardStore(rl) if err != nil { return } err = userSpace.collectWorkingDir(rl) if err != nil { return } _, err = ctx.Client.UserSpace().Create(userSpace.UserSpaceCreate) if err != nil { fmt.Printf("\033[31m保存配置失败: %v\033[0m", err) return } fmt.Println("\033[32m配置保存成功!\033[0m") } func promptSelectStorage() (string, error) { rl, _ := readline.NewEx(&readline.Config{ Prompt: "\033[36m»\033[0m ", HistoryFile: "/tmp/storage_history.txt", InterruptPrompt: "^C", }) defer rl.Close() fmt.Println("\033[1;36m请选择存储类型(StorageType):\033[0m") options := []string{"Local", "OBS", "OSS", "COS", "EFile", "S3"} for i, option := range options { fmt.Printf("\033[33m%d. %s\033[0m\n", i+1, option) } for { line, err := rl.Readline() if err != nil { return "", err } trimmed := strings.TrimSpace(line) switch trimmed { case "1": return "Local", nil case "2": return "OBS", nil case "3": return "OSS", nil case "4": return "COS", nil case "5": return "EFile", nil case "6": return "S3", nil default: fmt.Printf("\033[31m错误: 无效选项 '%s',请输入序号!\033[0m\n", line) } } } func (userSpace *MyUserSpace) collectLocalConfig(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入StorageName: \033[0m") storageName, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Location: \033[0m") location, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入RootDir: \033[0m") rootDir, err := rl.Readline() if err != nil { return err } userSpace.Storage = &cortypes.LocalType{ Type: "Local", Location: cortypes.Location{ StorageName: storageName, Location: location, }, } userSpace.Credential = &cortypes.LocalCred{ Type: "Local", RootDir: rootDir, } return nil } func (userSpace *MyUserSpace) collectObsConfig(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入Region: \033[0m") region, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Endpoint: \033[0m") endpoint, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Bucket: \033[0m") bucket, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入ProjectID: \033[0m") projectID, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m") accessKey, err := rl.Readline() if err != nil { return err } secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m") if err != nil { return err } secretKey := string(secretBytes) userSpace.Storage = &cortypes.OBSType{ Type: "OBS", Region: region, Endpoint: endpoint, Bucket: bucket, ProjectID: projectID, } userSpace.Credential = &cortypes.OBSCred{ Type: "OBS", AK: accessKey, SK: secretKey, } for { rl.SetPrompt("\033[36m是否支持存储服务间直传文件?(y/n): \033[0m") input, err := rl.Readline() if err != nil { return err } switch strings.ToLower(strings.TrimSpace(input)) { case "y", "yes": userSpace.Features = append(userSpace.Features, &cortypes.S2STransferFeature{ Type: "S2STransfer", }) return nil case "n", "no": fmt.Println("\033[36m不支持存储服务间直传文件 \033[0m") return nil default: fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m") } } } func (userSpace *MyUserSpace) collectOssConfig(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入Region: \033[0m") region, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Endpoint: \033[0m") endpoint, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Bucket: \033[0m") bucket, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m") accessKey, err := rl.Readline() if err != nil { return err } secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m") if err != nil { return err } secretKey := string(secretBytes) userSpace.Storage = &cortypes.OSSType{ Region: region, Endpoint: endpoint, Bucket: bucket, } userSpace.Credential = &cortypes.OSSCred{ Type: "OSS", AK: accessKey, SK: secretKey, } return nil } func (userSpace *MyUserSpace) collectCosConfig(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入Region: \033[0m") region, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Endpoint: \033[0m") endpoint, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Bucket: \033[0m") bucket, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m") accessKey, err := rl.Readline() if err != nil { return err } secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m") if err != nil { return err } secretKey := string(secretBytes) userSpace.Storage = &cortypes.COSType{ Type: "COS", Region: region, Endpoint: endpoint, Bucket: bucket, } userSpace.Credential = &cortypes.COSCred{ Type: "COS", AK: accessKey, SK: secretKey, } return nil } func (userSpace *MyUserSpace) collectEfileConfig(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入ClusterID: \033[0m") clusterID, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入TokenURL: \033[0m") tokenURL, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入APIURL: \033[0m") apiURL, err := rl.Readline() if err != nil { return err } tokenExpire := 0 for { rl.SetPrompt("\033[36m请输入TokenExpire: \033[0m") valueInt, err := rl.Readline() if err != nil { return err } if strings.TrimSpace(valueInt) == "" { fmt.Println("\033[31m错误:输入不能为空,请输入正整数\033[0m") continue } num, err := strconv.ParseInt(valueInt, 10, 64) if err != nil { fmt.Printf("\033[31m错误:'%s' 不是有效整数,请输入正整数\033[0m\n", valueInt) continue } if num <= 0 { fmt.Printf("\033[31m错误:%d 不是正整数,请输入大于 0 的整数\033[0m\n", num) continue } tokenExpire = int(num) break } rl.SetPrompt("\033[36m请输入User: \033[0m") user, err := rl.Readline() if err != nil { return err } passwordBytes, err := rl.ReadPassword("\033[36m请输入Password: \033[0m") if err != nil { return err } password := string(passwordBytes) rl.SetPrompt("\033[36m请输入OrgID: \033[0m") orgID, err := rl.Readline() if err != nil { return err } userSpace.Storage = &cortypes.EFileType{ Type: "EFile", ClusterID: clusterID, } userSpace.Credential = &cortypes.EFileCred{ Type: "EFile", TokenURL: tokenURL, APIURL: apiURL, TokenExpire: tokenExpire, User: user, Password: password, OrgID: orgID, } for { rl.SetPrompt("\033[36m是否提供能进行EC计算的接口?(y/n): \033[0m") input, err := rl.Readline() if err != nil { return err } switch strings.ToLower(strings.TrimSpace(input)) { case "y", "yes": userSpace.Features = append(userSpace.Features, &cortypes.ECMultiplierFeature{ Type: "ECMultiplier", }) return nil case "n", "no": fmt.Println("\033[36m未提供能进行EC计算的接口 \033[0m") return nil default: fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m") } } } func (userSpace *MyUserSpace) collectS3Config(rl *readline.Instance) error { var err error rl.SetPrompt("\033[36m请输入Region: \033[0m") region, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Endpoint: \033[0m") endpoint, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入Bucket: \033[0m") bucket, err := rl.Readline() if err != nil { return err } rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m") accessKey, err := rl.Readline() if err != nil { return err } secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m") if err != nil { return err } secretKey := string(secretBytes) userSpace.Storage = &cortypes.S3Type{ Type: "S3", Region: region, Endpoint: endpoint, Bucket: bucket, } userSpace.Credential = &cortypes.S3Cred{ Type: "S3", AK: accessKey, SK: secretKey, } for { rl.SetPrompt("\033[36m是否支持分段上传?(y/n): \033[0m") input, err := rl.Readline() if err != nil { return err } switch strings.ToLower(strings.TrimSpace(input)) { case "y", "yes": userSpace.Features = append(userSpace.Features, &cortypes.MultipartUploadFeature{ Type: "MultipartUpload", }) return nil case "n", "no": fmt.Println("\033[36m不支持分段上传 \033[0m") return nil default: fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m") } } } func (userSpace *MyUserSpace) collectShardStore(rl *readline.Instance) error { for { rl.SetPrompt("\033[36m是否开启分片存储功能?(y/n): \033[0m") input, err := rl.Readline() if err != nil { return err } switch strings.ToLower(strings.TrimSpace(input)) { case "y", "yes": for { rl.SetPrompt("\033[36m请输入最大Size: \033[0m") sizeInput, err := rl.Readline() if err != nil { return err } if strings.TrimSpace(sizeInput) == "" { fmt.Println("\033[31m错误:输入不能为空31m错误:输入不能为空,请输入正整数\033[0m") continue } maxSize, err := strconv.ParseInt(sizeInput, 10, 64) if err != nil { fmt.Printf("\033[31m错误:'%s' 不是有效整数,请输入正整数\033[0m\n", sizeInput) continue } if maxSize <= 0 { fmt.Printf("\033[31m错误:%d 不是正整数,请输入大于 0 的整数\033[0m\n", maxSize) continue } userSpace.ShardStore = &cortypes.ShardStoreUserConfig{ MaxSize: maxSize, } return nil } case "n", "no": fmt.Println("\033[31m分片存储未启用 \033[0m") return nil default: fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m") } } } func (userSpace *MyUserSpace) collectWorkingDir(rl *readline.Instance) error { for { rl.SetPrompt("\033[36m默认工作路径(WorkingDir)为jcs,是否修改?(y/n): \033[0m") input, err := rl.Readline() if err != nil { return err } switch strings.ToLower(strings.TrimSpace(input)) { case "y", "yes": rl.SetPrompt("\033[36m请输入新的工作路径(WorkingDir): \033[0m") newValue, err := rl.Readline() if err != nil { return err } if newValue != "" { userSpace.WorkingDir = newValue } return nil case "n", "no": userSpace.WorkingDir = "jcs" return nil default: fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m") } } }