You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

create.go 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. package userspace
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "github.com/chzyer/readline"
  7. "github.com/spf13/cobra"
  8. "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
  9. cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
  10. "gitlink.org.cn/cloudream/jcs-pub/jcsctl/cmd"
  11. )
  12. type MyUserSpace struct {
  13. api.UserSpaceCreate
  14. }
  15. func init() {
  16. cmd := cobra.Command{
  17. Use: "create",
  18. Short: "add a new cloud storage",
  19. Run: func(c *cobra.Command, args []string) {
  20. ctx := cmd.GetCmdCtx(c)
  21. create(c, ctx)
  22. },
  23. }
  24. UserSpaceCmd.AddCommand(&cmd)
  25. }
  26. func create(c *cobra.Command, ctx *cmd.CommandContext) {
  27. var userSpace MyUserSpace
  28. rl, err := readline.New("> ")
  29. if err != nil {
  30. fmt.Printf("初始化命令行失败: %v\n", err)
  31. return
  32. }
  33. defer rl.Close()
  34. rl.SetPrompt("\033[36m请输入存储服务名称(Name): \033[0m")
  35. name, err := rl.Readline()
  36. if err != nil {
  37. return
  38. }
  39. userSpace.Name = name
  40. storageType, err := promptSelectStorage()
  41. if err != nil {
  42. return
  43. }
  44. switch storageType {
  45. case "Local":
  46. err = userSpace.collectLocalConfig(rl)
  47. case "OBS":
  48. err = userSpace.collectObsConfig(rl)
  49. case "OSS":
  50. err = userSpace.collectOssConfig(rl)
  51. case "COS":
  52. err = userSpace.collectCosConfig(rl)
  53. case "EFile":
  54. err = userSpace.collectEfileConfig(rl)
  55. case "S3":
  56. err = userSpace.collectS3Config(rl)
  57. }
  58. if err != nil {
  59. return
  60. }
  61. err = userSpace.collectShardStore(rl)
  62. if err != nil {
  63. return
  64. }
  65. err = userSpace.collectWorkingDir(rl)
  66. if err != nil {
  67. return
  68. }
  69. _, err = ctx.Client.UserSpaceCreate(userSpace.UserSpaceCreate)
  70. if err != nil {
  71. fmt.Printf("\033[31m保存配置失败: %v\033[0m", err)
  72. return
  73. }
  74. fmt.Println("\033[32m配置保存成功!\033[0m")
  75. }
  76. func promptSelectStorage() (string, error) {
  77. rl, _ := readline.NewEx(&readline.Config{
  78. Prompt: "\033[36m»\033[0m ",
  79. HistoryFile: "/tmp/storage_history.txt",
  80. InterruptPrompt: "^C",
  81. })
  82. defer rl.Close()
  83. fmt.Println("\033[1;36m请选择存储类型(StorageType):\033[0m")
  84. options := []string{"Local", "OBS", "OSS", "COS", "EFile", "S3"}
  85. for i, option := range options {
  86. fmt.Printf("\033[33m%d. %s\033[0m\n", i+1, option)
  87. }
  88. for {
  89. line, err := rl.Readline()
  90. if err != nil {
  91. return "", err
  92. }
  93. trimmed := strings.TrimSpace(line)
  94. switch trimmed {
  95. case "1":
  96. return "Local", nil
  97. case "2":
  98. return "OBS", nil
  99. case "3":
  100. return "OSS", nil
  101. case "4":
  102. return "COS", nil
  103. case "5":
  104. return "EFile", nil
  105. case "6":
  106. return "S3", nil
  107. default:
  108. fmt.Printf("\033[31m错误: 无效选项 '%s',请输入序号!\033[0m\n", line)
  109. }
  110. }
  111. }
  112. func (userSpace *MyUserSpace) collectLocalConfig(rl *readline.Instance) error {
  113. var err error
  114. rl.SetPrompt("\033[36m请输入StorageName: \033[0m")
  115. storageName, err := rl.Readline()
  116. if err != nil {
  117. return err
  118. }
  119. rl.SetPrompt("\033[36m请输入Location: \033[0m")
  120. location, err := rl.Readline()
  121. if err != nil {
  122. return err
  123. }
  124. rl.SetPrompt("\033[36m请输入RootDir: \033[0m")
  125. rootDir, err := rl.Readline()
  126. if err != nil {
  127. return err
  128. }
  129. userSpace.Storage = &cortypes.LocalType{
  130. Type: "Local",
  131. Location: cortypes.Location{
  132. StorageName: storageName,
  133. Location: location,
  134. },
  135. }
  136. userSpace.Credential = &cortypes.LocalCred{
  137. Type: "Local",
  138. RootDir: rootDir,
  139. }
  140. return nil
  141. }
  142. func (userSpace *MyUserSpace) collectObsConfig(rl *readline.Instance) error {
  143. var err error
  144. rl.SetPrompt("\033[36m请输入Region: \033[0m")
  145. region, err := rl.Readline()
  146. if err != nil {
  147. return err
  148. }
  149. rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
  150. endpoint, err := rl.Readline()
  151. if err != nil {
  152. return err
  153. }
  154. rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
  155. bucket, err := rl.Readline()
  156. if err != nil {
  157. return err
  158. }
  159. rl.SetPrompt("\033[36m请输入ProjectID: \033[0m")
  160. projectID, err := rl.Readline()
  161. if err != nil {
  162. return err
  163. }
  164. rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
  165. accessKey, err := rl.Readline()
  166. if err != nil {
  167. return err
  168. }
  169. secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
  170. if err != nil {
  171. return err
  172. }
  173. secretKey := string(secretBytes)
  174. userSpace.Storage = &cortypes.OBSType{
  175. Type: "OBS",
  176. Region: region,
  177. Endpoint: endpoint,
  178. Bucket: bucket,
  179. ProjectID: projectID,
  180. }
  181. userSpace.Credential = &cortypes.OBSCred{
  182. Type: "OBS",
  183. AK: accessKey,
  184. SK: secretKey,
  185. }
  186. for {
  187. rl.SetPrompt("\033[36m是否支持存储服务间直传文件?(y/n): \033[0m")
  188. input, err := rl.Readline()
  189. if err != nil {
  190. return err
  191. }
  192. switch strings.ToLower(strings.TrimSpace(input)) {
  193. case "y", "yes":
  194. userSpace.Features = append(userSpace.Features, &cortypes.S2STransferFeature{
  195. Type: "S2STransfer",
  196. })
  197. return nil
  198. case "n", "no":
  199. fmt.Println("\033[36m不支持存储服务间直传文件 \033[0m")
  200. return nil
  201. default:
  202. fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
  203. }
  204. }
  205. }
  206. func (userSpace *MyUserSpace) collectOssConfig(rl *readline.Instance) error {
  207. var err error
  208. rl.SetPrompt("\033[36m请输入Region: \033[0m")
  209. region, err := rl.Readline()
  210. if err != nil {
  211. return err
  212. }
  213. rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
  214. endpoint, err := rl.Readline()
  215. if err != nil {
  216. return err
  217. }
  218. rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
  219. bucket, err := rl.Readline()
  220. if err != nil {
  221. return err
  222. }
  223. rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
  224. accessKey, err := rl.Readline()
  225. if err != nil {
  226. return err
  227. }
  228. secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
  229. if err != nil {
  230. return err
  231. }
  232. secretKey := string(secretBytes)
  233. userSpace.Storage = &cortypes.OSSType{
  234. Region: region,
  235. Endpoint: endpoint,
  236. Bucket: bucket,
  237. }
  238. userSpace.Credential = &cortypes.OSSCred{
  239. Type: "OSS",
  240. AK: accessKey,
  241. SK: secretKey,
  242. }
  243. return nil
  244. }
  245. func (userSpace *MyUserSpace) collectCosConfig(rl *readline.Instance) error {
  246. var err error
  247. rl.SetPrompt("\033[36m请输入Region: \033[0m")
  248. region, err := rl.Readline()
  249. if err != nil {
  250. return err
  251. }
  252. rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
  253. endpoint, err := rl.Readline()
  254. if err != nil {
  255. return err
  256. }
  257. rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
  258. bucket, err := rl.Readline()
  259. if err != nil {
  260. return err
  261. }
  262. rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
  263. accessKey, err := rl.Readline()
  264. if err != nil {
  265. return err
  266. }
  267. secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
  268. if err != nil {
  269. return err
  270. }
  271. secretKey := string(secretBytes)
  272. userSpace.Storage = &cortypes.COSType{
  273. Type: "COS",
  274. Region: region,
  275. Endpoint: endpoint,
  276. Bucket: bucket,
  277. }
  278. userSpace.Credential = &cortypes.COSCred{
  279. Type: "COS",
  280. AK: accessKey,
  281. SK: secretKey,
  282. }
  283. return nil
  284. }
  285. func (userSpace *MyUserSpace) collectEfileConfig(rl *readline.Instance) error {
  286. var err error
  287. rl.SetPrompt("\033[36m请输入ClusterID: \033[0m")
  288. clusterID, err := rl.Readline()
  289. if err != nil {
  290. return err
  291. }
  292. rl.SetPrompt("\033[36m请输入TokenURL: \033[0m")
  293. tokenURL, err := rl.Readline()
  294. if err != nil {
  295. return err
  296. }
  297. rl.SetPrompt("\033[36m请输入APIURL: \033[0m")
  298. apiURL, err := rl.Readline()
  299. if err != nil {
  300. return err
  301. }
  302. tokenExpire := 0
  303. for {
  304. rl.SetPrompt("\033[36m请输入TokenExpire: \033[0m")
  305. valueInt, err := rl.Readline()
  306. if err != nil {
  307. return err
  308. }
  309. if strings.TrimSpace(valueInt) == "" {
  310. fmt.Println("\033[31m错误:输入不能为空,请输入正整数\033[0m")
  311. continue
  312. }
  313. num, err := strconv.ParseInt(valueInt, 10, 64)
  314. if err != nil {
  315. fmt.Printf("\033[31m错误:'%s' 不是有效整数,请输入正整数\033[0m\n", valueInt)
  316. continue
  317. }
  318. if num <= 0 {
  319. fmt.Printf("\033[31m错误:%d 不是正整数,请输入大于 0 的整数\033[0m\n", num)
  320. continue
  321. }
  322. tokenExpire = int(num)
  323. break
  324. }
  325. rl.SetPrompt("\033[36m请输入User: \033[0m")
  326. user, err := rl.Readline()
  327. if err != nil {
  328. return err
  329. }
  330. passwordBytes, err := rl.ReadPassword("\033[36m请输入Password: \033[0m")
  331. if err != nil {
  332. return err
  333. }
  334. password := string(passwordBytes)
  335. rl.SetPrompt("\033[36m请输入OrgID: \033[0m")
  336. orgID, err := rl.Readline()
  337. if err != nil {
  338. return err
  339. }
  340. userSpace.Storage = &cortypes.EFileType{
  341. Type: "EFile",
  342. ClusterID: clusterID,
  343. }
  344. userSpace.Credential = &cortypes.EFileCred{
  345. Type: "EFile",
  346. TokenURL: tokenURL,
  347. APIURL: apiURL,
  348. TokenExpire: tokenExpire,
  349. User: user,
  350. Password: password,
  351. OrgID: orgID,
  352. }
  353. for {
  354. rl.SetPrompt("\033[36m是否提供能进行EC计算的接口?(y/n): \033[0m")
  355. input, err := rl.Readline()
  356. if err != nil {
  357. return err
  358. }
  359. switch strings.ToLower(strings.TrimSpace(input)) {
  360. case "y", "yes":
  361. userSpace.Features = append(userSpace.Features, &cortypes.ECMultiplierFeature{
  362. Type: "ECMultiplier",
  363. })
  364. return nil
  365. case "n", "no":
  366. fmt.Println("\033[36m未提供能进行EC计算的接口 \033[0m")
  367. return nil
  368. default:
  369. fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
  370. }
  371. }
  372. }
  373. func (userSpace *MyUserSpace) collectS3Config(rl *readline.Instance) error {
  374. var err error
  375. rl.SetPrompt("\033[36m请输入Region: \033[0m")
  376. region, err := rl.Readline()
  377. if err != nil {
  378. return err
  379. }
  380. rl.SetPrompt("\033[36m请输入Endpoint: \033[0m")
  381. endpoint, err := rl.Readline()
  382. if err != nil {
  383. return err
  384. }
  385. rl.SetPrompt("\033[36m请输入Bucket: \033[0m")
  386. bucket, err := rl.Readline()
  387. if err != nil {
  388. return err
  389. }
  390. rl.SetPrompt("\033[36m请输入AccessKeyID: \033[0m")
  391. accessKey, err := rl.Readline()
  392. if err != nil {
  393. return err
  394. }
  395. secretBytes, err := rl.ReadPassword("\033[36m请输入AccessKeySecret: \033[0m")
  396. if err != nil {
  397. return err
  398. }
  399. secretKey := string(secretBytes)
  400. userSpace.Storage = &cortypes.S3Type{
  401. Type: "S3",
  402. Region: region,
  403. Endpoint: endpoint,
  404. Bucket: bucket,
  405. }
  406. userSpace.Credential = &cortypes.S3Cred{
  407. Type: "S3",
  408. AK: accessKey,
  409. SK: secretKey,
  410. }
  411. for {
  412. rl.SetPrompt("\033[36m是否支持分段上传?(y/n): \033[0m")
  413. input, err := rl.Readline()
  414. if err != nil {
  415. return err
  416. }
  417. switch strings.ToLower(strings.TrimSpace(input)) {
  418. case "y", "yes":
  419. userSpace.Features = append(userSpace.Features, &cortypes.MultipartUploadFeature{
  420. Type: "MultipartUpload",
  421. })
  422. return nil
  423. case "n", "no":
  424. fmt.Println("\033[36m不支持分段上传 \033[0m")
  425. return nil
  426. default:
  427. fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
  428. }
  429. }
  430. }
  431. func (userSpace *MyUserSpace) collectShardStore(rl *readline.Instance) error {
  432. for {
  433. rl.SetPrompt("\033[36m是否开启分片存储功能?(y/n): \033[0m")
  434. input, err := rl.Readline()
  435. if err != nil {
  436. return err
  437. }
  438. switch strings.ToLower(strings.TrimSpace(input)) {
  439. case "y", "yes":
  440. for {
  441. rl.SetPrompt("\033[36m请输入最大Size: \033[0m")
  442. sizeInput, err := rl.Readline()
  443. if err != nil {
  444. return err
  445. }
  446. if strings.TrimSpace(sizeInput) == "" {
  447. fmt.Println("\033[31m错误:输入不能为空31m错误:输入不能为空,请输入正整数\033[0m")
  448. continue
  449. }
  450. maxSize, err := strconv.ParseInt(sizeInput, 10, 64)
  451. if err != nil {
  452. fmt.Printf("\033[31m错误:'%s' 不是有效整数,请输入正整数\033[0m\n", sizeInput)
  453. continue
  454. }
  455. if maxSize <= 0 {
  456. fmt.Printf("\033[31m错误:%d 不是正整数,请输入大于 0 的整数\033[0m\n", maxSize)
  457. continue
  458. }
  459. userSpace.ShardStore = &cortypes.ShardStoreUserConfig{
  460. MaxSize: maxSize,
  461. }
  462. return nil
  463. }
  464. case "n", "no":
  465. fmt.Println("\033[31m分片存储未启用 \033[0m")
  466. return nil
  467. default:
  468. fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
  469. }
  470. }
  471. }
  472. func (userSpace *MyUserSpace) collectWorkingDir(rl *readline.Instance) error {
  473. for {
  474. rl.SetPrompt("\033[36m默认工作路径(WorkingDir)为jcs,是否修改?(y/n): \033[0m")
  475. input, err := rl.Readline()
  476. if err != nil {
  477. return err
  478. }
  479. switch strings.ToLower(strings.TrimSpace(input)) {
  480. case "y", "yes":
  481. rl.SetPrompt("\033[36m请输入新的工作路径(WorkingDir): \033[0m")
  482. newValue, err := rl.Readline()
  483. if err != nil {
  484. return err
  485. }
  486. if newValue != "" {
  487. userSpace.WorkingDir = newValue
  488. }
  489. return nil
  490. case "n", "no":
  491. userSpace.WorkingDir = "jcs"
  492. return nil
  493. default:
  494. fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
  495. }
  496. }
  497. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。