yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/aws_request.go (about) 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 15 package aws 16 17 import ( 18 "encoding/xml" 19 "io" 20 "io/ioutil" 21 "net/url" 22 "strings" 23 24 "github.com/aws/aws-sdk-go/aws" 25 "github.com/aws/aws-sdk-go/aws/awserr" 26 "github.com/aws/aws-sdk-go/aws/client" 27 "github.com/aws/aws-sdk-go/aws/client/metadata" 28 "github.com/aws/aws-sdk-go/aws/corehandlers" 29 "github.com/aws/aws-sdk-go/aws/request" 30 v4 "github.com/aws/aws-sdk-go/aws/signer/v4" 31 "github.com/aws/aws-sdk-go/private/protocol/query" 32 xj "github.com/basgys/goxml2json" 33 34 "yunion.io/x/jsonutils" 35 "yunion.io/x/log" 36 "yunion.io/x/pkg/errors" 37 38 "yunion.io/x/cloudmux/pkg/cloudprovider" 39 ) 40 41 var UnmarshalHandler = request.NamedHandler{Name: "yunion.query.Unmarshal", Fn: Unmarshal} 42 43 func Unmarshal(r *request.Request) { 44 defer r.HTTPResponse.Body.Close() 45 if r.DataFilled() { 46 var decoder *xml.Decoder 47 if DEBUG { 48 body, err := ioutil.ReadAll(r.HTTPResponse.Body) 49 if err != nil { 50 r.Error = awserr.NewRequestFailure( 51 awserr.New("ioutil.ReadAll", "read response body", err), 52 r.HTTPResponse.StatusCode, 53 r.RequestID, 54 ) 55 return 56 } 57 log.Debugf("response: \n%s", string(body)) 58 decoder = xml.NewDecoder(strings.NewReader(string(body))) 59 } else { 60 decoder = xml.NewDecoder(r.HTTPResponse.Body) 61 } 62 if r.ClientInfo.ServiceID == EC2_SERVICE_ID { 63 err := decoder.Decode(r.Data) 64 if err != nil { 65 r.Error = awserr.NewRequestFailure( 66 awserr.New("SerializationError", "failed decoding EC2 Query response", err), 67 r.HTTPResponse.StatusCode, 68 r.RequestID, 69 ) 70 } 71 return 72 } 73 for { 74 tok, err := decoder.Token() 75 if err != nil { 76 if err == io.EOF { 77 break 78 } 79 r.Error = awserr.NewRequestFailure( 80 awserr.New("decoder.Token()", "get token", err), 81 r.HTTPResponse.StatusCode, 82 r.RequestID, 83 ) 84 return 85 } 86 87 if tok == nil { 88 break 89 } 90 91 switch typed := tok.(type) { 92 case xml.CharData: 93 continue 94 case xml.StartElement: 95 if typed.Name.Local == r.Operation.Name+"Result" { 96 err = decoder.DecodeElement(r.Data, &typed) 97 if err != nil { 98 r.Error = awserr.NewRequestFailure( 99 awserr.New("DecodeElement", "failed decoding Query response", err), 100 r.HTTPResponse.StatusCode, 101 r.RequestID, 102 ) 103 } 104 return 105 } 106 case xml.EndElement: 107 break 108 } 109 } 110 111 } 112 } 113 114 var buildHandler = request.NamedHandler{Name: "yunion.query.Build", Fn: Build} 115 116 func Build(r *request.Request) { 117 body := url.Values{ 118 "Action": {r.Operation.Name}, 119 "Version": {r.ClientInfo.APIVersion}, 120 } 121 if r.Params != nil { 122 if params, ok := r.Params.(map[string]string); ok { 123 for k, v := range params { 124 body.Add(k, v) 125 } 126 } 127 } 128 129 if DEBUG { 130 log.Debugf("params: %s", body.Encode()) 131 } 132 133 if !r.IsPresigned() { 134 r.HTTPRequest.Method = "POST" 135 r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8") 136 r.SetBufferBody([]byte(body.Encode())) 137 } else { // This is a pre-signed request 138 r.HTTPRequest.Method = "GET" 139 r.HTTPRequest.URL.RawQuery = body.Encode() 140 } 141 } 142 143 var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.ec2query.UnmarshalError", Fn: UnmarshalError} 144 145 type sAwsError struct { 146 Errors struct { 147 Type string 148 Code string 149 Message string 150 } `json:"Error"` 151 RequestID string 152 } 153 154 func (self sAwsError) Error() string { 155 return jsonutils.Marshal(self).String() 156 } 157 158 func UnmarshalError(r *request.Request) { 159 defer r.HTTPResponse.Body.Close() 160 161 result, err := xj.Convert(r.HTTPResponse.Body) 162 if err != nil { 163 r.Error = errors.Wrapf(err, "xj.Convert") 164 return 165 } 166 167 obj, err := jsonutils.Parse([]byte(result.String())) 168 if err != nil { 169 r.Error = errors.Wrapf(err, "jsonutils.Parse") 170 return 171 } 172 173 respErr := &sAwsError{} 174 if obj.Contains("ErrorResponse") { 175 err = obj.Unmarshal(respErr, "ErrorResponse") 176 if err != nil { 177 r.Error = errors.Wrapf(err, "obj.Unmarshal") 178 return 179 } 180 } else if obj.Contains("Response", "Errors") { 181 err = obj.Unmarshal(respErr, "Response", "Errors") 182 if err != nil { 183 r.Error = errors.Wrapf(err, "obj.Unmarshal") 184 return 185 } 186 } 187 188 if strings.Contains(respErr.Errors.Code, "NotFound") || respErr.Errors.Code == "NoSuchEntity" { 189 r.Error = errors.Wrapf(cloudprovider.ErrNotFound, jsonutils.Marshal(respErr).String()) 190 return 191 } 192 193 r.Error = respErr 194 return 195 } 196 197 func (self *SAwsClient) request(regionId, serviceName, serviceId, apiVersion string, apiName string, params map[string]string, retval interface{}, assumeRole bool) error { 198 if len(regionId) == 0 { 199 regionId = self.getDefaultRegionId() 200 } 201 session, err := self.getAwsSession(regionId, assumeRole) 202 if err != nil { 203 return err 204 } 205 c := session.ClientConfig(serviceName) 206 metadata := metadata.ClientInfo{ 207 ServiceName: serviceName, 208 ServiceID: serviceId, 209 SigningName: c.SigningName, 210 SigningRegion: c.SigningRegion, 211 Endpoint: c.Endpoint, 212 APIVersion: apiVersion, 213 } 214 215 if self.debug { 216 logLevel := aws.LogLevelType(uint(aws.LogDebugWithRequestErrors) + uint(aws.LogDebugWithHTTPBody)) 217 c.Config.LogLevel = &logLevel 218 } 219 220 client := client.New(*c.Config, metadata, c.Handlers) 221 client.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) 222 client.Handlers.Build.PushBackNamed(buildHandler) 223 client.Handlers.Unmarshal.PushBackNamed(UnmarshalHandler) 224 client.Handlers.UnmarshalMeta.PushBackNamed(query.UnmarshalMetaHandler) 225 client.Handlers.UnmarshalError.PushBackNamed(UnmarshalErrorHandler) 226 client.Handlers.Validate.Remove(corehandlers.ValidateEndpointHandler) 227 return jsonRequest(client, apiName, params, retval, true) 228 } 229 230 func jsonRequest(cli *client.Client, apiName string, params map[string]string, retval interface{}, debug bool) error { 231 op := &request.Operation{ 232 Name: apiName, 233 HTTPMethod: "POST", 234 HTTPPath: "/", 235 Paginator: &request.Paginator{ 236 InputTokens: []string{"NextToken"}, 237 OutputTokens: []string{"NextToken"}, 238 LimitToken: "MaxResults", 239 TruncationToken: "", 240 }, 241 } 242 243 req := cli.NewRequest(op, params, retval) 244 err := req.Send() 245 if err != nil { 246 if e, ok := err.(awserr.RequestFailure); ok && e.StatusCode() == 404 { 247 return cloudprovider.ErrNotFound 248 } 249 return err 250 } 251 return nil 252 }