request.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ecloud
  15. import (
  16. "bytes"
  17. "io"
  18. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/httputils"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. )
  25. type SJSONRequest struct {
  26. SBaseRequest
  27. Data jsonutils.JSONObject
  28. }
  29. func NewJSONRequest(data jsonutils.JSONObject) *SJSONRequest {
  30. jr := &SJSONRequest{
  31. Data: data,
  32. SBaseRequest: *NewBaseRequest(),
  33. }
  34. headers := jr.GetHeaders()
  35. headers["Content-Type"] = "application/json"
  36. return jr
  37. }
  38. func newBaseJSONRequest(regionId string, endpoint string, port string, serverPath string, query map[string]string, data jsonutils.JSONObject) SJSONRequest {
  39. req := *NewJSONRequest(data)
  40. req.SBaseRequest.Endpoint = endpoint
  41. req.SBaseRequest.Port = port
  42. req.SBaseRequest.RegionId = regionId
  43. req.SBaseRequest.ServerPath = serverPath
  44. if data != nil {
  45. req.SBaseRequest.Content = []byte(data.String())
  46. }
  47. mergeMap(req.GetQueryParams(), query)
  48. return req
  49. }
  50. func (jr *SJSONRequest) GetBodyReader() io.Reader {
  51. if jr.Data == nil {
  52. return nil
  53. }
  54. return strings.NewReader(jr.Data.String())
  55. }
  56. // SOpenApiInstanceRequest 用于调用新的 OpenAPI 云主机实例列表接口:
  57. // 走区域 endpoint (api-xxx.cmecloud.cn:8443),路径为 /api/openapi-instance/v4/list/describe-instances
  58. type SOpenApiInstanceRequest struct {
  59. SJSONRequest
  60. RegionId string
  61. }
  62. // SOpenApiInstanceActionRequest 用于 OpenAPI Instance 区域接口(与实例列表一致走 api-*.cmecloud.cn:8443)。
  63. // 可用于除 list/describe-instances 之外的其他 instance openapi path。
  64. type SOpenApiInstanceActionRequest struct {
  65. SJSONRequest
  66. RegionId string
  67. ServerPath string
  68. }
  69. func NewOpenApiInstanceRequest(regionId string, data jsonutils.JSONObject) *SOpenApiInstanceRequest {
  70. host := openApiRegionHost(regionId)
  71. r := SOpenApiInstanceRequest{RegionId: regionId}
  72. r.SJSONRequest = newBaseJSONRequest(regionId, host, "8443", "/api/openapi-instance/v4/list/describe-instances", nil, data)
  73. return &r
  74. }
  75. func (rr *SOpenApiInstanceRequest) GetPort() string {
  76. return "8443"
  77. }
  78. func (rr *SOpenApiInstanceRequest) GetEndpoint() string {
  79. return openApiRegionHost(rr.RegionId)
  80. }
  81. func (rr *SOpenApiInstanceRequest) GetHeaders() map[string]string {
  82. h := rr.SJSONRequest.GetHeaders()
  83. h["Region-Id"] = rr.RegionId
  84. // OpenAPI 示例中带有 User-Agent,但不是必须字段,这里不强制设置
  85. return h
  86. }
  87. func NewOpenApiInstanceActionRequest(regionId string, serverPath string, data jsonutils.JSONObject) *SOpenApiInstanceActionRequest {
  88. host := openApiRegionHost(regionId)
  89. r := SOpenApiInstanceActionRequest{RegionId: regionId, ServerPath: serverPath}
  90. r.SJSONRequest = newBaseJSONRequest(regionId, host, "8443", serverPath, nil, data)
  91. return &r
  92. }
  93. func (rr *SOpenApiInstanceActionRequest) GetPort() string { return "8443" }
  94. func (rr *SOpenApiInstanceActionRequest) GetEndpoint() string { return openApiRegionHost(rr.RegionId) }
  95. func (rr *SOpenApiInstanceActionRequest) GetServerPath() string { return rr.ServerPath }
  96. func (rr *SOpenApiInstanceActionRequest) GetHeaders() map[string]string {
  97. h := rr.SJSONRequest.GetHeaders()
  98. if len(rr.RegionId) > 0 {
  99. h["Region-Id"] = rr.RegionId
  100. }
  101. return h
  102. }
  103. func (r *SOpenApiInstanceActionRequest) Base() *SBaseRequest { return &r.SJSONRequest.SBaseRequest }
  104. // openApiRegionHostFallback 与官方 SDK 不一致的 regionId -> 主机名(省/区名与机房城市名不同时需单独映射),先查表再走拼接。
  105. var openApiRegionHostFallback = map[string]string{
  106. "cn-beijing-1": "api-beijing-2.cmecloud.cn", // 华北北京3
  107. "cn-jiangsu-1": "api-wuxi-1.cmecloud.cn", // 华东苏州
  108. "cn-guangdong-1": "api-dongguan-1.cmecloud.cn", // 华南广州3
  109. "cn-sichuan-1": "api-yaan-1.cmecloud.cn", // 西南成都
  110. "cn-henan-1": "api-zhengzhou-1.cmecloud.cn", // 华中郑州
  111. "cn-hunan-1": "api-zhuzhou-1.cmecloud.cn", // 华中长沙2
  112. "cn-shandong-1": "api-jinan-1.cmecloud.cn", // 华东济南
  113. "cn-shaanxi-1": "api-xian-1.cmecloud.cn", // 西北西安(陕 vs 山)
  114. "cn-shangxi-1": "api-shanxi-1.cmecloud.cn", // 山西太原
  115. "cn-zhejiang-1": "api-ningbo-1.cmecloud.cn", // 华东杭州
  116. "cn-yunnan-1": "api-yunnan-2.cmecloud.cn", // 云南昆明2
  117. "cn-neimenggu-1": "api-huhehaote-1.cmecloud.cn", // 华北呼和浩特
  118. "cn-guzhou-1": "api-guiyang-1.cmecloud.cn", // 西南贵阳(贵州->贵阳)
  119. "cn-hubei-2": "api-wuhan-1.cmecloud.cn", // 湖北武汉
  120. }
  121. // openApiRegionHost 由 regionId 得到区域 API 主机:先查 fallback(与官方 SDK 一致的特殊项),再按 api-{suffix}.cmecloud.cn 拼接,避免 API 返回新 region 拿不到 endpoint。
  122. func openApiRegionHost(regionId string) string {
  123. if host := openApiRegionHostFallback[regionId]; host != "" {
  124. return host
  125. }
  126. suffix := strings.TrimPrefix(regionId, "cn-")
  127. if suffix == "" {
  128. suffix = regionId
  129. }
  130. return "api-" + suffix + ".cmecloud.cn"
  131. }
  132. // openApiVpcConsoleHost 用于 OpenAPI VPC:ecloudsdkvpc 使用 console-*.cmecloud.cn 且会正确转发 POST body,api-* 网关可能不转 body 导致“参数为空”。
  133. var openApiVpcConsoleHostFallback = map[string]string{
  134. "cn-beijing-1": "console-beijing-2.cmecloud.cn",
  135. "cn-jiangsu-1": "console-wuxi-1.cmecloud.cn",
  136. "cn-guangdong-1": "console-dongguan-1.cmecloud.cn",
  137. "cn-sichuan-1": "console-yaan-1.cmecloud.cn",
  138. "cn-henan-1": "console-zhengzhou-1.cmecloud.cn",
  139. "cn-hunan-1": "console-zhuzhou-1.cmecloud.cn",
  140. "cn-shandong-1": "console-jinan-1.cmecloud.cn",
  141. "cn-shaanxi-1": "console-xian-1.cmecloud.cn",
  142. "cn-shanghai-1": "console-shanghai-1.cmecloud.cn",
  143. "cn-chongqing-1": "console-chongqing-1.cmecloud.cn",
  144. "cn-zhejiang-1": "console-ningbo-1.cmecloud.cn",
  145. "cn-tianjin-1": "console-tianjin-1.cmecloud.cn",
  146. "cn-jilin-1": "console-jilin-1.cmecloud.cn",
  147. "cn-hubei-2": "console-hubei-1.cmecloud.cn",
  148. "cn-jiangxi-1": "console-jiangxi-1.cmecloud.cn",
  149. "cn-gansu-1": "console-gansu-1.cmecloud.cn",
  150. "cn-shangxi-1": "console-shanxi-1.cmecloud.cn",
  151. "cn-liaoning-1": "console-liaoning-1.cmecloud.cn",
  152. "cn-yunnan-1": "console-yunnan-2.cmecloud.cn",
  153. "cn-hebei-1": "console-hebei-1.cmecloud.cn",
  154. "cn-fujian-1": "console-fujian-1.cmecloud.cn",
  155. "cn-guangxi-1": "console-guangxi-1.cmecloud.cn",
  156. "cn-anhui-1": "console-anhui-1.cmecloud.cn",
  157. "cn-neimenggu-1": "console-huhehaote-1.cmecloud.cn",
  158. "cn-guzhou-1": "console-guiyang-1.cmecloud.cn",
  159. "cn-hainan-1": "console-hainan-1.cmecloud.cn",
  160. "cn-xinjiang-1": "console-xinjiang-1.cmecloud.cn",
  161. }
  162. func openApiVpcConsoleHost(regionId string) string {
  163. if host := openApiVpcConsoleHostFallback[regionId]; host != "" {
  164. return host
  165. }
  166. suffix := strings.TrimPrefix(regionId, "cn-")
  167. if suffix == "" {
  168. suffix = regionId
  169. }
  170. return "console-" + suffix + ".cmecloud.cn"
  171. }
  172. // SOpenApiRegionRequest 用于调用新的 OpenAPI 区域列表接口:
  173. // 需走区域 endpoint (api-xxx.cmecloud.cn:8443),与 yunion.io/x/ecloud 成功样例一致。
  174. type SOpenApiRegionRequest struct {
  175. SJSONRequest
  176. RegionId string
  177. }
  178. func NewOpenApiRegionRequest(regionId string, data jsonutils.JSONObject) *SOpenApiRegionRequest {
  179. host := openApiRegionHost(regionId)
  180. r := SOpenApiRegionRequest{
  181. SJSONRequest: *NewJSONRequest(data),
  182. RegionId: regionId,
  183. }
  184. r.SBaseRequest.Port = "8443"
  185. r.SBaseRequest.Endpoint = host
  186. r.SBaseRequest.RegionId = regionId
  187. r.ServerPath = "/api/openapi-instance/v4/region/describe-regions"
  188. return &r
  189. }
  190. func (rr *SOpenApiRegionRequest) GetPort() string {
  191. return "8443"
  192. }
  193. func (rr *SOpenApiRegionRequest) GetEndpoint() string {
  194. return openApiRegionHost(rr.RegionId)
  195. }
  196. func (rr *SOpenApiRegionRequest) GetHeaders() map[string]string {
  197. h := rr.SJSONRequest.GetHeaders()
  198. if len(rr.RegionId) > 0 {
  199. h["Region-Id"] = rr.RegionId
  200. }
  201. return h
  202. }
  203. // SOpenApiZoneRequest 用于 OpenAPI 可用区列表:GET 区域 endpoint 上的 describe-zones,与 describe-regions 同机房子网。
  204. type SOpenApiZoneRequest struct {
  205. SJSONRequest
  206. RegionId string
  207. }
  208. func NewOpenApiZoneRequest(regionId string, data jsonutils.JSONObject) *SOpenApiZoneRequest {
  209. host := openApiRegionHost(regionId)
  210. r := SOpenApiZoneRequest{
  211. SJSONRequest: *NewJSONRequest(data),
  212. RegionId: regionId,
  213. }
  214. r.SBaseRequest.Port = "8443"
  215. r.SBaseRequest.Endpoint = host
  216. r.SBaseRequest.RegionId = regionId
  217. r.ServerPath = "/api/openapi-instance/v4/region/describe-zones"
  218. return &r
  219. }
  220. func (rr *SOpenApiZoneRequest) GetPort() string { return "8443" }
  221. func (rr *SOpenApiZoneRequest) GetEndpoint() string { return openApiRegionHost(rr.RegionId) }
  222. func (rr *SOpenApiZoneRequest) GetHeaders() map[string]string {
  223. h := rr.SJSONRequest.GetHeaders()
  224. if len(rr.RegionId) > 0 {
  225. h["Region-Id"] = rr.RegionId
  226. }
  227. return h
  228. }
  229. func (r *SOpenApiZoneRequest) Base() *SBaseRequest {
  230. return &r.SJSONRequest.SBaseRequest
  231. }
  232. // SOpenApiRequest 通用 OpenAPI 请求,用于 ecloud.10086.cn 上任意 path(GET/POST)。
  233. // 新接口迁移时可直接使用,无需再新增专用 Request 类型。
  234. type SOpenApiRequest struct {
  235. SJSONRequest
  236. RegionId string
  237. ServerPath string
  238. }
  239. func NewOpenApiRequest(regionId string, serverPath string, data jsonutils.JSONObject) *SOpenApiRequest {
  240. r := SOpenApiRequest{RegionId: regionId, ServerPath: serverPath}
  241. r.SJSONRequest = newBaseJSONRequest(regionId, "ecloud.10086.cn", "", serverPath, nil, data)
  242. return &r
  243. }
  244. func (rr *SOpenApiRequest) GetPort() string { return "" }
  245. func (rr *SOpenApiRequest) GetEndpoint() string {
  246. return "ecloud.10086.cn"
  247. }
  248. func (rr *SOpenApiRequest) GetServerPath() string { return rr.ServerPath }
  249. func (rr *SOpenApiRequest) GetRegionId() string { return rr.RegionId }
  250. func (rr *SOpenApiRequest) GetHeaders() map[string]string {
  251. h := rr.SJSONRequest.GetHeaders()
  252. if len(rr.RegionId) > 0 {
  253. h["Region-Id"] = rr.RegionId
  254. }
  255. return h
  256. }
  257. // regionIdToPoolId 创建 VPC 时 body.region 需为资源池 ID(与 ecloudsdkvpc initRegions 一致)
  258. var regionIdToPoolId = map[string]string{
  259. "cn-beijing-1": "CIDC-RP-29", // 北京2
  260. "cn-jiangsu-1": "CIDC-RP-25", // 无锡
  261. "cn-guangdong-1": "CIDC-RP-26", // 东莞
  262. "cn-sichuan-1": "CIDC-RP-27", // 雅安
  263. "cn-henan-1": "CIDC-RP-28", // 郑州
  264. "cn-hunan-1": "CIDC-RP-30", // 株洲
  265. "cn-shandong-1": "CIDC-RP-31", // 济南
  266. "cn-shaanxi-1": "CIDC-RP-32", // 西安
  267. "cn-shanghai-1": "CIDC-RP-33",
  268. "cn-chongqing-1": "CIDC-RP-34",
  269. "cn-zhejiang-1": "CIDC-RP-35", // 宁波
  270. "cn-tianjin-1": "CIDC-RP-36",
  271. "cn-jilin-1": "CIDC-RP-37",
  272. "cn-hubei-2": "CIDC-RP-38", // 湖北
  273. "cn-jiangxi-1": "CIDC-RP-39",
  274. "cn-gansu-1": "CIDC-RP-40",
  275. "cn-shangxi-1": "CIDC-RP-41", // 山西
  276. "cn-liaoning-1": "CIDC-RP-42",
  277. "cn-yunnan-1": "CIDC-RP-43",
  278. "cn-hebei-1": "CIDC-RP-44",
  279. "cn-fujian-1": "CIDC-RP-45",
  280. "cn-guangxi-1": "CIDC-RP-46",
  281. "cn-anhui-1": "CIDC-RP-47",
  282. "cn-neimenggu-1": "CIDC-RP-48", // 呼和浩特
  283. "cn-guzhou-1": "CIDC-RP-49", // 贵阳
  284. "cn-hainan-1": "CIDC-RP-53",
  285. "cn-xinjiang-1": "CIDC-RP-54",
  286. }
  287. // SOpenApiVpcRequest 用于 OpenAPI VPC 接口:与 ecloudsdkvpc 一致使用 console-*.cmecloud.cn:8443,否则 api-* 网关可能不转发 POST body 导致“可用区region不能为空”。
  288. type SOpenApiVpcRequest struct {
  289. SJSONRequest
  290. RegionId string
  291. ServerPath string
  292. }
  293. func (rr *SOpenApiVpcRequest) GetHeaders() map[string]string {
  294. h := rr.SJSONRequest.GetHeaders()
  295. if len(rr.RegionId) > 0 {
  296. h["Region-Id"] = rr.RegionId
  297. }
  298. return h
  299. }
  300. func newOpenApiConsoleRequest(regionId string, serverPath string, query map[string]string, data jsonutils.JSONObject) (host string, req SJSONRequest) {
  301. host = openApiVpcConsoleHost(regionId)
  302. req = newBaseJSONRequest(regionId, host, "8443", serverPath, query, data)
  303. return host, req
  304. }
  305. func NewOpenApiVpcRequest(regionId string, serverPath string, query map[string]string, data jsonutils.JSONObject) *SOpenApiVpcRequest {
  306. _, req := newOpenApiConsoleRequest(regionId, serverPath, query, data)
  307. r := SOpenApiVpcRequest{
  308. SJSONRequest: req,
  309. RegionId: regionId,
  310. ServerPath: serverPath,
  311. }
  312. return &r
  313. }
  314. func (rr *SOpenApiVpcRequest) GetPort() string { return "8443" }
  315. func (rr *SOpenApiVpcRequest) GetEndpoint() string { return openApiVpcConsoleHost(rr.RegionId) }
  316. func (rr *SOpenApiVpcRequest) Base() *SBaseRequest { return &rr.SJSONRequest.SBaseRequest }
  317. // SOpenApiEbsRequest 用于 EBS/磁盘/快照 OpenAPI:与 ecloudsdkebs 一致使用 console-*.cmecloud.cn:8443。
  318. type SOpenApiEbsRequest struct {
  319. SJSONRequest
  320. RegionId string
  321. ServerPath string
  322. }
  323. func NewOpenApiEbsRequest(regionId string, serverPath string, query map[string]string, data jsonutils.JSONObject) *SOpenApiEbsRequest {
  324. _, req := newOpenApiConsoleRequest(regionId, serverPath, query, data)
  325. r := SOpenApiEbsRequest{
  326. SJSONRequest: req,
  327. RegionId: regionId,
  328. ServerPath: serverPath,
  329. }
  330. return &r
  331. }
  332. func (rr *SOpenApiEbsRequest) GetPort() string { return "8443" }
  333. func (rr *SOpenApiEbsRequest) GetEndpoint() string { return openApiVpcConsoleHost(rr.RegionId) }
  334. func (rr *SOpenApiEbsRequest) Base() *SBaseRequest { return &rr.SJSONRequest.SBaseRequest }
  335. func (rr *SOpenApiEbsRequest) GetHeaders() map[string]string {
  336. h := rr.SJSONRequest.GetHeaders()
  337. if len(rr.RegionId) > 0 {
  338. h["Region-Id"] = rr.RegionId
  339. }
  340. return h
  341. }
  342. // SOpenApiMopcRequest 用于 MOPC 开放接口(如账户余额查询)。ecloudsdkmopc 中 CIDC-CORE-00 对应 ecloud.10086.cn,/api/openapi-mop/ 仅在该网关可路由,console-* 会报 M02C002 gateway cannot route。
  343. type SOpenApiMopcRequest struct {
  344. SJSONRequest
  345. RegionId string
  346. ServerPath string
  347. }
  348. const mopcBalanceQueryPath = "/api/openapi-mop/openapi"
  349. func NewOpenApiMopcBalanceRequest(regionId string, userId string) *SOpenApiMopcRequest {
  350. body := jsonutils.Marshal(map[string]interface{}{
  351. "balanceQueryPOSTBody": map[string]string{"userId": userId},
  352. "balanceQueryPOSTHeader": map[string]string{},
  353. })
  354. r := SOpenApiMopcRequest{RegionId: regionId, ServerPath: mopcBalanceQueryPath}
  355. r.SJSONRequest = newBaseJSONRequest(regionId, "ecloud.10086.cn", "", mopcBalanceQueryPath, nil, body)
  356. r.SBaseRequest.QueryParams["method"] = "SYAN_UNHT_balancequeryOpen"
  357. r.SBaseRequest.QueryParams["format"] = "json"
  358. r.SBaseRequest.QueryParams["status"] = "1"
  359. return &r
  360. }
  361. func (rr *SOpenApiMopcRequest) GetPort() string { return "" }
  362. func (rr *SOpenApiMopcRequest) GetEndpoint() string { return "ecloud.10086.cn" }
  363. func (rr *SOpenApiMopcRequest) Base() *SBaseRequest { return &rr.SJSONRequest.SBaseRequest }
  364. func (rr *SOpenApiMopcRequest) GetHeaders() map[string]string {
  365. h := rr.SJSONRequest.GetHeaders()
  366. if len(rr.RegionId) > 0 {
  367. h["Region-Id"] = rr.RegionId
  368. }
  369. return h
  370. }
  371. func mergeMap(m1, m2 map[string]string) {
  372. if m2 == nil {
  373. return
  374. }
  375. for k, v := range m2 {
  376. m1[k] = v
  377. }
  378. }
  379. type SBaseRequest struct {
  380. Method string
  381. Endpoint string
  382. ServerPath string
  383. Port string
  384. RegionId string
  385. ReadTimeout time.Duration
  386. ConnectTimeout time.Duration
  387. isInsecure *bool
  388. QueryParams map[string]string
  389. Headers map[string]string
  390. Content []byte
  391. }
  392. func NewBaseRequest() *SBaseRequest {
  393. return &SBaseRequest{
  394. QueryParams: map[string]string{},
  395. Headers: map[string]string{},
  396. }
  397. }
  398. func (br *SBaseRequest) GetMethod() string {
  399. return br.Method
  400. }
  401. func (br *SBaseRequest) SetMethod(method string) {
  402. br.Method = method
  403. }
  404. func (br *SBaseRequest) GetEndpoint() string {
  405. return br.Endpoint
  406. }
  407. func (br *SBaseRequest) GetServerPath() string {
  408. return br.ServerPath
  409. }
  410. func (br *SBaseRequest) GetPort() string {
  411. return br.Port
  412. }
  413. func (br *SBaseRequest) GetRegionId() string {
  414. return br.RegionId
  415. }
  416. func (br *SBaseRequest) GetHeaders() map[string]string {
  417. return br.Headers
  418. }
  419. func (br *SBaseRequest) GetQueryParams() map[string]string {
  420. return br.QueryParams
  421. }
  422. func (br *SBaseRequest) GetBodyReader() io.Reader {
  423. if len(br.Content) > 0 {
  424. return bytes.NewReader(br.Content)
  425. }
  426. return nil
  427. }
  428. func (br *SBaseRequest) GetVersion() string {
  429. return "2016-12-05"
  430. }
  431. func (br *SBaseRequest) GetTimestamp() string {
  432. sh, _ := time.LoadLocation("Asia/Shanghai")
  433. return time.Now().In(sh).Format("2006-01-02T15:04:05Z")
  434. }
  435. func (br *SBaseRequest) GetReadTimeout() time.Duration {
  436. return br.ReadTimeout
  437. }
  438. func (br *SBaseRequest) GetConnectTimeout() time.Duration {
  439. return br.ConnectTimeout
  440. }
  441. func (br *SBaseRequest) GetHTTPSInsecure() bool {
  442. if br.isInsecure == nil {
  443. return false
  444. }
  445. return *br.isInsecure
  446. }
  447. func (br *SBaseRequest) SetHTTPSInsecure(isInsecure bool) {
  448. br.isInsecure = &isInsecure
  449. }
  450. func (br *SBaseRequest) GetUserAgent() map[string]string {
  451. return nil
  452. }
  453. // Base 返回底层的 SBaseRequest 指针,便于客户端统一处理。
  454. func (jr *SJSONRequest) Base() *SBaseRequest {
  455. return &jr.SBaseRequest
  456. }
  457. func (r *SOpenApiInstanceRequest) Base() *SBaseRequest {
  458. return &r.SJSONRequest.SBaseRequest
  459. }
  460. func (r *SOpenApiRegionRequest) Base() *SBaseRequest {
  461. return &r.SJSONRequest.SBaseRequest
  462. }
  463. func (r *SOpenApiRequest) Base() *SBaseRequest {
  464. return &r.SJSONRequest.SBaseRequest
  465. }
  466. func (br *SBaseRequest) ForMateResponseBody(jrbody jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  467. if jrbody == nil || !jrbody.Contains("state") {
  468. return nil, ErrMissKey{
  469. Key: "state",
  470. Jo: jrbody,
  471. }
  472. }
  473. state, _ := jrbody.GetString("state")
  474. switch state {
  475. case "OK":
  476. if !jrbody.Contains("body") {
  477. // 部分接口(如 DELETE)仅返回 state:OK,无 body
  478. return jsonutils.NewDict(), nil
  479. }
  480. body, _ := jrbody.Get("body")
  481. return body, nil
  482. default:
  483. if jrbody.Contains("errorMessage") {
  484. msg, _ := jrbody.GetString("errorMessage")
  485. if strings.Contains(msg, "Invalid parameter AccessKey") {
  486. return nil, errors.Wrapf(cloudprovider.ErrInvalidAccessKey, "%s", msg)
  487. }
  488. return nil, &httputils.JSONClientError{Code: 400, Details: msg}
  489. }
  490. return nil, &httputils.JSONClientError{Code: 400, Details: jrbody.String()}
  491. }
  492. }