Skip to content

Commit 20a14e4

Browse files
authored
{otsutil} Add otsutil for tablestore management (#1274)
* add ots * adjust ak update * use cli blob * add test mock * add test mock * fix tests * fix tests
1 parent 6287819 commit 20a14e4

File tree

8 files changed

+1640
-2
lines changed

8 files changed

+1640
-2
lines changed

main/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/aliyun/aliyun-cli/v3/openapi"
3030
"github.com/aliyun/aliyun-cli/v3/oss/lib"
3131
"github.com/aliyun/aliyun-cli/v3/ossutil"
32+
"github.com/aliyun/aliyun-cli/v3/otsutil"
3233
)
3334

3435
func Main(args []string) {
@@ -79,6 +80,8 @@ func Main(args []string) {
7980
rootCmd.AddSubCommand(go_migrate.NewGoMigrateCommand())
8081
// new oss command
8182
rootCmd.AddSubCommand(ossutil.NewOssutilCommand())
83+
// tablestore command
84+
rootCmd.AddSubCommand(otsutil.NewOtsutilCommand())
8285
if os.Getenv("GENERATE_METADATA") == "YES" {
8386
generateMetadata(rootCmd)
8487
} else {

ossutil/ossutil2.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/alibabacloud-go/tea/tea"
1818
"github.com/aliyun/aliyun-cli/v3/cli"
1919
"github.com/aliyun/aliyun-cli/v3/config"
20+
"github.com/aliyun/aliyun-cli/v3/util"
2021
)
2122

2223
type Context struct {
@@ -327,9 +328,9 @@ func DownloadAndUnzip(url string, destFile string, exeFilePath string, extractCe
327328
return fmt.Errorf("failed to remove existing file %s: %v", exeFilePath, err)
328329
}
329330
}
330-
err = os.Rename(sourceFile, exeFilePath)
331+
err = util.CopyFileAndRemoveSource(sourceFile, exeFilePath)
331332
if err != nil {
332-
return fmt.Errorf("failed to move file from %s to %s: %v", sourceFile, exeFilePath, err)
333+
return err
333334
}
334335
// set exec permission
335336
if runtime.GOOS != "windows" {

otsutil/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package otsutil
2+
3+
import (
4+
"github.com/aliyun/aliyun-cli/v3/cli"
5+
"github.com/aliyun/aliyun-cli/v3/i18n"
6+
)
7+
8+
func NewOtsutilCommand() *cli.Command {
9+
return &cli.Command{
10+
Name: "otsutil",
11+
Short: i18n.T("Alibaba Cloud Tablestore Utility", "阿里云表格存储工具"),
12+
Usage: "aliyun otsutil <command> [args...]",
13+
Hidden: false,
14+
Run: func(ctx *cli.Context, args []string) error {
15+
if ctx.IsHelp() {
16+
hasHelp := false
17+
for i, arg := range args {
18+
if arg == "help" {
19+
hasHelp = true
20+
break
21+
} else if arg == "--help" {
22+
// 将 --help 替换为 help
23+
args[i] = "help"
24+
hasHelp = true
25+
break
26+
}
27+
}
28+
// 如果没有找到 help 相关参数,说明是被过滤掉的 "help" 参数
29+
if !hasHelp {
30+
args = append(args, "help")
31+
}
32+
}
33+
// fmt.Println("otsutil args", args)
34+
options := NewContext(ctx)
35+
return options.Run(args)
36+
},
37+
// allow unknown args
38+
EnableUnknownFlag: true,
39+
KeepArgs: true,
40+
SkipDefaultHelp: true, // DO NOT use default help and version
41+
}
42+
}

otsutil/main_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package otsutil
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/aliyun/aliyun-cli/v3/cli"
10+
)
11+
12+
func TestNewOtsCommand(t *testing.T) {
13+
cmd := NewOtsutilCommand()
14+
if cmd == nil {
15+
t.Fatalf("NewOtsutilCommand returned nil")
16+
}
17+
if cmd.Name != "otsutil" {
18+
t.Errorf("Name expected 'otsutil', got %s", cmd.Name)
19+
}
20+
if cmd.Short == nil {
21+
t.Fatalf("Short i18n text nil")
22+
}
23+
if en := cmd.Short.Get("en"); en != "Alibaba Cloud Tablestore Utility" {
24+
t.Errorf("Short en expected 'Alibaba Cloud Tablestore Utility', got %s", en)
25+
}
26+
if zh := cmd.Short.Get("zh"); zh != "阿里云表格存储工具" {
27+
t.Errorf("Short zh expected '阿里云表格存储工具', got %s", zh)
28+
}
29+
if cmd.Usage != "aliyun otsutil <command> [args...]" {
30+
t.Errorf("Usage expected 'aliyun otsutil <command> [args...]', got %s", cmd.Usage)
31+
}
32+
if cmd.Hidden {
33+
t.Errorf("Hidden expected false")
34+
}
35+
if !cmd.EnableUnknownFlag {
36+
t.Errorf("EnableUnknownFlag expected true")
37+
}
38+
if !cmd.KeepArgs {
39+
t.Errorf("KeepArgs expected true")
40+
}
41+
if !cmd.SkipDefaultHelp {
42+
t.Errorf("SkipDefaultHelp expected true")
43+
}
44+
if cmd.Run == nil {
45+
t.Errorf("Run function should not be nil")
46+
}
47+
}
48+
49+
func TestNewOtsCommandMetadata(t *testing.T) {
50+
cmd := NewOtsutilCommand()
51+
metaMap := map[string]*cli.Metadata{}
52+
cmd.GetMetadata(metaMap)
53+
m, ok := metaMap[cmd.Name]
54+
if !ok {
55+
t.Fatalf("metadata for %s not found", cmd.Name)
56+
}
57+
if m.Name != "otsutil" {
58+
t.Errorf("metadata name expected otsutil, got %s", m.Name)
59+
}
60+
if m.Usage != cmd.Usage {
61+
t.Errorf("metadata usage mismatch")
62+
}
63+
if m.Hidden != cmd.Hidden {
64+
t.Errorf("metadata hidden mismatch")
65+
}
66+
if se := m.Short["en"]; se != "Alibaba Cloud Tablestore Utility" {
67+
t.Errorf("metadata short en mismatch: %s", se)
68+
}
69+
if sz := m.Short["zh"]; sz != "阿里云表格存储工具" {
70+
t.Errorf("metadata short zh mismatch: %s", sz)
71+
}
72+
}
73+
74+
func TestOtsCommandRunInstalledSkipNetwork(t *testing.T) {
75+
// 准备临时目录作为配置路径
76+
tmpDir := t.TempDir()
77+
oldGet := getConfigurePathFunc
78+
getConfigurePathFunc = func() string { return tmpDir }
79+
defer func() { getConfigurePathFunc = oldGet }()
80+
81+
// 创建假可执行文件(ts)
82+
execPath := filepath.Join(tmpDir, "ts")
83+
if err := os.WriteFile(execPath, []byte("#!/bin/sh\necho dummy\n"), 0755); err != nil {
84+
t.Fatalf("write fake exec: %v", err)
85+
}
86+
87+
// 设置忽略profile,避免真实配置依赖
88+
os.Setenv("ALIBABA_CLOUD_IGNORE_PROFILE", "TRUE")
89+
defer os.Unsetenv("ALIBABA_CLOUD_IGNORE_PROFILE")
90+
91+
cmd := NewOtsutilCommand()
92+
stdout := &bytes.Buffer{}
93+
stderr := &bytes.Buffer{}
94+
ctx := cli.NewCommandContext(stdout, stderr)
95+
96+
// 直接调用Run函数(不经过Command.Execute解析)
97+
err := cmd.Run(ctx, []string{})
98+
if err == nil {
99+
t.Fatalf("expected error, got nil")
100+
}
101+
errStr := err.Error()
102+
if !bytes.Contains([]byte(errStr), []byte("profile default is not configure yet")) &&
103+
!bytes.Contains([]byte(errStr), []byte("can't get credential")) {
104+
t.Errorf("unexpected error: %v", err)
105+
}
106+
}

0 commit comments

Comments
 (0)