@@ -6,20 +6,54 @@ import (
66 "fmt"
77 "io"
88 "io/ioutil"
9+ "slices"
10+ "strconv"
911 "strings"
12+ "sync"
13+ "sync/atomic"
14+ "time"
1015
1116 "github.com/spf13/cobra"
17+ "github.com/ucloud/ucloud-sdk-go/ucloud"
18+ "github.com/ucloud/ucloud-sdk-go/ucloud/request"
1219
1320 "github.com/ucloud/ucloud-cli/base"
21+ "github.com/ucloud/ucloud-cli/model/status"
22+ "github.com/ucloud/ucloud-cli/ux"
1423)
1524
25+ type RepeatsConfig struct {
26+ Poller * base.Poller
27+ IDInResp string
28+ }
29+
30+ var RepeatsSupportedAPI = map [string ]RepeatsConfig {
31+ "CreateULHostInstance" : {Poller : ulhostSpoller , IDInResp : "ULHostId" },
32+ }
33+
34+ const ActionField = "Action"
35+ const RepeatsField = "repeats"
36+ const ConcurrentField = "concurrent"
37+ const DefaultConcurrent = 20
38+ const HelpField = "help"
39+ const HelpInfo = `Usage: ucloud api [options] --Action actionName --param1 value1 --param2 value2 ...
40+ Options:
41+ --local-file string the path of the local file which contains the api parameters
42+ --repeats string the number of repeats
43+ --concurrent string the number of concurrent
44+ --help show help`
45+
1646// NewCmdAPI ucloud api --xkey xvalue
1747func NewCmdAPI (out io.Writer ) * cobra.Command {
1848 return & cobra.Command {
1949 Use : "api" ,
2050 Short : "Call API" ,
2151 Long : "Call API" ,
2252 Run : func (c * cobra.Command , args []string ) {
53+ if slices .Contains (args , "--help" ) {
54+ fmt .Fprintln (out , HelpInfo )
55+ return
56+ }
2357 params , err := parseParamsFromCmdLine (args )
2458 if err != nil {
2559 fmt .Fprintln (out , err )
@@ -37,7 +71,36 @@ func NewCmdAPI(out io.Writer) *cobra.Command {
3771 return
3872 }
3973 }
40-
74+ if action , actionOK := params [ActionField ].(string ); actionOK {
75+ if repeatsConfig , repeatsSupported := RepeatsSupportedAPI [action ]; repeatsSupported {
76+ if repeats , repeatsOK := params [RepeatsField ].(string ); repeatsOK {
77+ var repeatsNum int
78+ var concurrentNum int
79+ repeatsNum , err = strconv .Atoi (repeats )
80+ if err != nil {
81+ fmt .Fprintf (out , "error: %v\n " , err )
82+ return
83+ }
84+ if concurrent , concurrentOK := params [ConcurrentField ].(string ); concurrentOK {
85+ concurrentNum , err = strconv .Atoi (concurrent )
86+ if err != nil {
87+ fmt .Fprintf (out , "error: %v\n " , err )
88+ return
89+ }
90+ } else {
91+ concurrentNum = DefaultConcurrent
92+ }
93+ delete (params , RepeatsField )
94+ delete (params , ConcurrentField )
95+ err = genericInvokeRepeatWrapper (& repeatsConfig , params , action , repeatsNum , concurrentNum )
96+ if err != nil {
97+ fmt .Fprintf (out , "error: %v\n " , err )
98+ return
99+ }
100+ return
101+ }
102+ }
103+ }
41104 req := base .BizClient .UAccountClient .NewGenericRequest ()
42105 err = req .SetPayload (params )
43106 if err != nil {
@@ -87,3 +150,104 @@ func parseParamsFromCmdLine(args []string) (map[string]interface{}, error) {
87150 }
88151 return params , nil
89152}
153+
154+ func genericInvokeRepeatWrapper (repeatsConfig * RepeatsConfig , params map [string ]interface {}, action string , repeats int , concurrent int ) error {
155+ if repeatsConfig == nil {
156+ return fmt .Errorf ("error: repeatsConfig is nil" )
157+ }
158+ if repeats <= 0 {
159+ return fmt .Errorf ("error: repeats should be a positive integer" )
160+ }
161+ if concurrent <= 0 {
162+ return fmt .Errorf ("error: concurrent should be a positive integer" )
163+ }
164+ wg := & sync.WaitGroup {}
165+ tokens := make (chan struct {}, concurrent )
166+ retCh := make (chan bool , repeats )
167+
168+ wg .Add (repeats )
169+ //ux.Doc.Disable()
170+ refresh := ux .NewRefresh ()
171+
172+ req := base .BizClient .UAccountClient .NewGenericRequest ()
173+ err := req .SetPayload (params )
174+ if err != nil {
175+ return fmt .Errorf ("fail to set payload: %w" , err )
176+ }
177+
178+ go func (req request.GenericRequest ) {
179+ for i := 0 ; i < repeats ; i ++ {
180+ go func (req request.GenericRequest , idx int ) {
181+ tokens <- struct {}{}
182+ defer func () {
183+ <- tokens
184+ //设置延时,使报错能渲染出来
185+ time .Sleep (time .Second / 5 )
186+ wg .Done ()
187+ }()
188+ success := true
189+ resp , err := base .BizClient .UAccountClient .GenericInvoke (req )
190+ block := ux .NewBlock ()
191+ ux .Doc .Append (block )
192+ logs := []string {"==================================================" }
193+ logs = append (logs , fmt .Sprintf ("api:%v, request:%v" , action , base .ToQueryMap (req )))
194+ if err != nil {
195+ logs = append (logs , fmt .Sprintf ("err:%v" , err ))
196+ block .Append (base .ParseError (err ))
197+ success = false
198+ } else {
199+ logs = append (logs , fmt .Sprintf ("resp:%#v" , resp ))
200+ resourceId , ok := resp .GetPayload ()[repeatsConfig .IDInResp ].(string )
201+ if ! ok {
202+ block .Append (fmt .Sprintf ("expect %v in response, but not found" , repeatsConfig .IDInResp ))
203+ success = false
204+ } else {
205+ text := fmt .Sprintf ("the resource[%s] is initializing" , resourceId )
206+ result := repeatsConfig .Poller .Sspoll (resourceId , text , []string {status .HOST_RUNNING , status .HOST_FAIL }, block , & request.CommonBase {
207+ Region : ucloud .String (req .GetRegion ()),
208+ Zone : ucloud .String (req .GetZone ()),
209+ ProjectId : ucloud .String (req .GetProjectId ()),
210+ })
211+ if result .Err != nil {
212+ success = false
213+ block .Append (result .Err .Error ())
214+ }
215+ }
216+ retCh <- success
217+ logs = append (logs , fmt .Sprintf ("index:%d, result:%t" , idx , success ))
218+ base .LogInfo (logs ... )
219+ }
220+ }(req , i )
221+ }
222+ }(req )
223+
224+ var success , fail atomic.Int32
225+ go func () {
226+ block := ux .NewBlock ()
227+ ux .Doc .Append (block )
228+ block .Append (fmt .Sprintf ("creating, total:%d, success:%d, fail:%d" , repeats , success .Load (), fail .Load ()))
229+ blockCount := ux .Doc .GetBlockCount ()
230+ for ret := range retCh {
231+ if ret {
232+ success .Add (1 )
233+ } else {
234+ fail .Add (1 )
235+ }
236+ text := fmt .Sprintf ("creating, total:%d, success:%d, fail:%d" , repeats , success .Load (), fail .Load ())
237+ if blockCount != ux .Doc .GetBlockCount () {
238+ block = ux .NewBlock ()
239+ ux .Doc .Append (block )
240+ block .Append (text )
241+ blockCount = ux .Doc .GetBlockCount ()
242+ } else {
243+ block .Update (text , 0 )
244+ }
245+ if repeats == int (success .Load ())+ int (fail .Load ()) && fail .Load () > 0 {
246+ fmt .Printf ("Check logs in %s\n " , base .GetLogFilePath ())
247+ }
248+ }
249+ }()
250+ wg .Wait ()
251+ refresh .Do (fmt .Sprintf ("finally, total:%d, success:%d, fail:%d" , repeats , success .Load (), repeats - int (success .Load ())))
252+ return nil
253+ }
0 commit comments