mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-21 01:27:15 +08:00
fix: json request 支持自定义匹配err
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/huawei/client/auth"
|
||||
@@ -133,6 +134,34 @@ func (self *SBaseManager) _get(request requests.IRequest, responseKey string) (j
|
||||
return self._do(request, responseKey)
|
||||
}
|
||||
|
||||
type HuaweiClientError struct {
|
||||
Code int
|
||||
Errorcode []string
|
||||
err error
|
||||
Details string
|
||||
}
|
||||
|
||||
func (ce *HuaweiClientError) Error() string {
|
||||
return jsonutils.Marshal(ce).String()
|
||||
}
|
||||
|
||||
func (ce *HuaweiClientError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error {
|
||||
err := body.Unmarshal(ce)
|
||||
if err != nil {
|
||||
ce.err = errors.Wrapf(err, "body.Unmarshal(%s)", body.String())
|
||||
ce.Code = statusCode
|
||||
ce.Details = body.String()
|
||||
return ce
|
||||
}
|
||||
if ce.Code == 0 {
|
||||
ce.Code = statusCode
|
||||
}
|
||||
if len(ce.Details) == 0 {
|
||||
ce.Details = body.String()
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
func (self *SBaseManager) jsonRequest(request requests.IRequest) (http.Header, jsonutils.JSONObject, error) {
|
||||
ctx := context.Background()
|
||||
// hook request
|
||||
@@ -162,11 +191,15 @@ func (self *SBaseManager) jsonRequest(request requests.IRequest) (http.Header, j
|
||||
log.Debugf("url: %s", request.BuildUrl())
|
||||
}
|
||||
|
||||
client := httputils.NewJsonClient(self.httpClient)
|
||||
req := httputils.NewJsonRequest(httputils.THttpMethod(request.GetMethod()), request.BuildUrl(), jsonBody)
|
||||
req.SetHeader(header)
|
||||
resp := &HuaweiClientError{}
|
||||
// 发送 request。todo: 支持debug
|
||||
const MAX_RETRY = 3
|
||||
retry := MAX_RETRY
|
||||
for {
|
||||
h, b, e := httputils.JSONRequest(self.httpClient, ctx, httputils.THttpMethod(request.GetMethod()), request.BuildUrl(), header, jsonBody, self.debug)
|
||||
h, b, e := client.Send(ctx, req, resp, self.debug)
|
||||
if e == nil {
|
||||
if self.debug {
|
||||
log.Debugf("response: %s body: %s", h, b)
|
||||
@@ -175,12 +208,12 @@ func (self *SBaseManager) jsonRequest(request requests.IRequest) (http.Header, j
|
||||
}
|
||||
|
||||
switch err := e.(type) {
|
||||
case *httputils.JSONClientError:
|
||||
case *HuaweiClientError:
|
||||
if err.Code == 499 && retry > 0 && request.GetMethod() == "GET" {
|
||||
retry -= 1
|
||||
time.Sleep(time.Second * time.Duration(MAX_RETRY-retry))
|
||||
} else if (err.Code == 404 || strings.Contains(err.Details, "could not be found") || strings.Contains(err.Details, "does not exist")) && request.GetMethod() != "POST" {
|
||||
return h, b, cloudprovider.ErrNotFound
|
||||
return h, b, errors.Wrap(cloudprovider.ErrNotFound, err.Error())
|
||||
} else {
|
||||
return h, b, e
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudprovider"
|
||||
"yunion.io/x/onecloud/pkg/multicloud/huawei/client/modules"
|
||||
)
|
||||
|
||||
type SLink struct {
|
||||
@@ -182,6 +183,10 @@ func (self *SHuaweiClient) CreateClouduser(name, password, desc string) (*SCloud
|
||||
user := SClouduser{client: self}
|
||||
err = DoCreate(client.Users.Create, jsonutils.Marshal(map[string]interface{}{"user": params}), &user)
|
||||
if err != nil {
|
||||
ce, ok := err.(*modules.HuaweiClientError)
|
||||
if ok && len(ce.Errorcode) > 0 && ce.Errorcode[0] == "1101" {
|
||||
return nil, errors.Wrap(err, `IAM user name. The length is between 5 and 32. The first digit is not a number. Special characters can only contain the '_' '-' or ' '`) //https://support.huaweicloud.com/api-iam/iam_08_0015.html
|
||||
}
|
||||
return nil, errors.Wrap(err, "DoCreate")
|
||||
}
|
||||
return &user, nil
|
||||
|
||||
@@ -239,7 +239,7 @@ func (region *SRegion) GetNatDEntryByID(id string) (SNatDEntry, error) {
|
||||
err := DoGet(region.ecsClient.DNatRules.Get, id, map[string]string{}, &dnat)
|
||||
|
||||
if err != nil {
|
||||
return SNatDEntry{}, cloudprovider.ErrNotFound
|
||||
return SNatDEntry{}, err
|
||||
}
|
||||
return dnat, nil
|
||||
}
|
||||
|
||||
@@ -81,6 +81,89 @@ type JSONClientErrorMsg struct {
|
||||
Error *JSONClientError
|
||||
}
|
||||
|
||||
type JsonClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
type JsonReuest interface {
|
||||
GetHttpMethod() THttpMethod
|
||||
GetRequestBody() jsonutils.JSONObject
|
||||
GetUrl() string
|
||||
SetHttpMethod(method THttpMethod)
|
||||
GetHeader() http.Header
|
||||
SetHeader(header http.Header)
|
||||
}
|
||||
|
||||
type JsonBaseRequest struct {
|
||||
httpMethod THttpMethod
|
||||
url string
|
||||
params interface{}
|
||||
header http.Header
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) GetHttpMethod() THttpMethod {
|
||||
return req.httpMethod
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) GetRequestBody() jsonutils.JSONObject {
|
||||
if req.params != nil {
|
||||
return jsonutils.Marshal(req.params)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) GetUrl() string {
|
||||
return req.url
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) SetHttpMethod(method THttpMethod) {
|
||||
req.httpMethod = method
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) GetHeader() http.Header {
|
||||
return req.header
|
||||
}
|
||||
|
||||
func (req *JsonBaseRequest) SetHeader(header http.Header) {
|
||||
for k, values := range header {
|
||||
req.header.Del(k)
|
||||
for _, v := range values {
|
||||
req.header.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewJsonRequest(method THttpMethod, url string, params interface{}) *JsonBaseRequest {
|
||||
return &JsonBaseRequest{
|
||||
httpMethod: method,
|
||||
url: url,
|
||||
params: params,
|
||||
header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
}
|
||||
}
|
||||
|
||||
type JsonResponse interface {
|
||||
ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error
|
||||
}
|
||||
|
||||
func (ce *JSONClientError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error {
|
||||
err := body.Unmarshal(ce)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "body.Unmarshal(%s)", body.String())
|
||||
}
|
||||
if ce.Code != 0 || len(ce.Class) > 0 || len(ce.Class) > 0 || len(ce.Details) > 0 {
|
||||
if ce.Code == 0 {
|
||||
ce.Code = statusCode
|
||||
}
|
||||
return ce
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewJsonClient(client *http.Client) *JsonClient {
|
||||
return &JsonClient{client: client}
|
||||
}
|
||||
|
||||
func (e *JSONClientError) Error() string {
|
||||
errMsg := JSONClientErrorMsg{Error: e}
|
||||
return jsonutils.Marshal(errMsg).String()
|
||||
@@ -383,6 +466,71 @@ func CloseResponse(resp *http.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func (client *JsonClient) Send(ctx context.Context, req JsonReuest, response JsonResponse, debug bool) (http.Header, jsonutils.JSONObject, error) {
|
||||
var bodystr string
|
||||
body := req.GetRequestBody()
|
||||
if !gotypes.IsNil(body) {
|
||||
bodystr = body.String()
|
||||
}
|
||||
jbody := strings.NewReader(bodystr)
|
||||
resp, err := Request(client.client, ctx, req.GetHttpMethod(), req.GetUrl(), req.GetHeader(), jbody, debug)
|
||||
if err != nil {
|
||||
ce := &JSONClientError{}
|
||||
ce.Code = 499
|
||||
ce.Details = err.Error()
|
||||
return nil, nil, ce
|
||||
}
|
||||
defer CloseResponse(resp)
|
||||
if debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
if resp.StatusCode < 300 {
|
||||
green(string(dump))
|
||||
} else if resp.StatusCode < 400 {
|
||||
yellow(string(dump))
|
||||
} else {
|
||||
red(string(dump))
|
||||
}
|
||||
}
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "Response body: %s\n", string(rbody))
|
||||
}
|
||||
if err != nil {
|
||||
ce := &JSONClientError{}
|
||||
ce.Code = resp.StatusCode
|
||||
ce.Details = fmt.Sprintf("Fail to read body: %v", err)
|
||||
return resp.Header, nil, ce
|
||||
}
|
||||
|
||||
var jrbody jsonutils.JSONObject = nil
|
||||
if len(rbody) > 0 && string(rbody[0]) == "{" {
|
||||
var err error
|
||||
jrbody, err = jsonutils.Parse(rbody)
|
||||
if err != nil {
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "parsing json %s failed: %v", string(rbody), err)
|
||||
}
|
||||
ce := &JSONClientError{}
|
||||
ce.Code = resp.StatusCode
|
||||
ce.Details = fmt.Sprintf("jsonutils.Parse(%s) error: %v", string(rbody), err)
|
||||
return resp.Header, nil, ce
|
||||
}
|
||||
} else {
|
||||
jrbody = jsonutils.NewDict()
|
||||
}
|
||||
|
||||
if resp.StatusCode < 300 {
|
||||
return resp.Header, jrbody, nil
|
||||
} else if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
||||
ce := JSONClientError{}
|
||||
ce.Code = resp.StatusCode
|
||||
ce.Details = resp.Header.Get("Location")
|
||||
ce.Class = "redirect"
|
||||
return resp.Header, nil, &ce
|
||||
}
|
||||
return resp.Header, jrbody, response.ParseErrorFromJsonResponse(resp.StatusCode, jrbody)
|
||||
}
|
||||
|
||||
func ParseResponse(resp *http.Response, err error, debug bool) (http.Header, []byte, error) {
|
||||
if err != nil {
|
||||
ce := JSONClientError{}
|
||||
|
||||
Reference in New Issue
Block a user