mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-07 22:24:32 +08:00
234 lines
6.3 KiB
Go
234 lines
6.3 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package aws
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/aws/client"
|
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
|
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
|
"github.com/aws/aws-sdk-go/private/protocol/query"
|
|
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
)
|
|
|
|
var UnmarshalHandler = request.NamedHandler{Name: "yunion.query.Unmarshal", Fn: Unmarshal}
|
|
|
|
func Unmarshal(r *request.Request) {
|
|
defer r.HTTPResponse.Body.Close()
|
|
if r.DataFilled() {
|
|
var decoder *xml.Decoder
|
|
if DEBUG {
|
|
body, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
|
if err != nil {
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New("ioutil.ReadAll", "read response body", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
return
|
|
}
|
|
log.Debugf("response: \n%s", string(body))
|
|
decoder = xml.NewDecoder(strings.NewReader(string(body)))
|
|
} else {
|
|
decoder = xml.NewDecoder(r.HTTPResponse.Body)
|
|
}
|
|
if r.ClientInfo.ServiceID == EC2_SERVICE_ID {
|
|
err := decoder.Decode(r.Data)
|
|
if err != nil {
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New("SerializationError", "failed decoding EC2 Query response", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
}
|
|
return
|
|
}
|
|
for {
|
|
tok, err := decoder.Token()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New("decoder.Token()", "get token", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
return
|
|
}
|
|
|
|
if tok == nil {
|
|
break
|
|
}
|
|
|
|
switch typed := tok.(type) {
|
|
case xml.CharData:
|
|
continue
|
|
case xml.StartElement:
|
|
if typed.Name.Local == r.Operation.Name+"Result" {
|
|
err = decoder.DecodeElement(r.Data, &typed)
|
|
if err != nil {
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New("DecodeElement", "failed decoding Query response", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
}
|
|
return
|
|
}
|
|
case xml.EndElement:
|
|
break
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
var buildHandler = request.NamedHandler{Name: "yunion.query.Build", Fn: Build}
|
|
|
|
func Build(r *request.Request) {
|
|
body := url.Values{
|
|
"Action": {r.Operation.Name},
|
|
"Version": {r.ClientInfo.APIVersion},
|
|
}
|
|
if r.Params != nil {
|
|
if params, ok := r.Params.(map[string]string); ok {
|
|
for k, v := range params {
|
|
body.Add(k, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
if DEBUG {
|
|
log.Debugf("params: %s", body.Encode())
|
|
}
|
|
|
|
if !r.IsPresigned() {
|
|
r.HTTPRequest.Method = "POST"
|
|
r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
|
r.SetBufferBody([]byte(body.Encode()))
|
|
} else { // This is a pre-signed request
|
|
r.HTTPRequest.Method = "GET"
|
|
r.HTTPRequest.URL.RawQuery = body.Encode()
|
|
}
|
|
}
|
|
|
|
var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.ec2query.UnmarshalError", Fn: UnmarshalError}
|
|
|
|
func UnmarshalError(r *request.Request) {
|
|
defer r.HTTPResponse.Body.Close()
|
|
|
|
respErr := &struct {
|
|
XMLName xml.Name `xml:"Response"`
|
|
Code string `xml:"Errors>Error>Code"`
|
|
Message string `xml:"Errors>Error>Message"`
|
|
RequestID string `xml:"RequestID"`
|
|
}{}
|
|
|
|
err := xmlutil.UnmarshalXMLError(&respErr, r.HTTPResponse.Body)
|
|
if err != nil {
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New(request.ErrCodeSerialization,
|
|
"failed to unmarshal error message", err),
|
|
r.HTTPResponse.StatusCode,
|
|
r.RequestID,
|
|
)
|
|
return
|
|
}
|
|
|
|
if strings.Contains(respErr.Code, "NotFound") {
|
|
r.Error = errors.Wrapf(cloudprovider.ErrNotFound, jsonutils.Marshal(respErr).String())
|
|
return
|
|
}
|
|
|
|
r.Error = awserr.NewRequestFailure(
|
|
awserr.New(respErr.Code, respErr.Message, nil),
|
|
r.HTTPResponse.StatusCode,
|
|
respErr.RequestID,
|
|
)
|
|
}
|
|
|
|
func (self *SAwsClient) request(regionId, serviceName, serviceId, apiVersion string, apiName string, params map[string]string, retval interface{}, assumeRole bool) error {
|
|
if len(regionId) == 0 {
|
|
regionId = self.getDefaultRegionId()
|
|
}
|
|
session, err := self.getAwsSession(regionId, assumeRole)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c := session.ClientConfig(serviceName)
|
|
metadata := metadata.ClientInfo{
|
|
ServiceName: serviceName,
|
|
ServiceID: serviceId,
|
|
SigningName: c.SigningName,
|
|
SigningRegion: c.SigningRegion,
|
|
Endpoint: c.Endpoint,
|
|
APIVersion: apiVersion,
|
|
}
|
|
|
|
if self.debug {
|
|
logLevel := aws.LogLevelType(uint(aws.LogDebugWithRequestErrors) + uint(aws.LogDebugWithHTTPBody))
|
|
c.Config.LogLevel = &logLevel
|
|
}
|
|
|
|
client := client.New(*c.Config, metadata, c.Handlers)
|
|
client.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
|
|
client.Handlers.Build.PushBackNamed(buildHandler)
|
|
client.Handlers.Unmarshal.PushBackNamed(UnmarshalHandler)
|
|
client.Handlers.UnmarshalMeta.PushBackNamed(query.UnmarshalMetaHandler)
|
|
client.Handlers.UnmarshalError.PushBackNamed(UnmarshalErrorHandler)
|
|
client.Handlers.Validate.Remove(corehandlers.ValidateEndpointHandler)
|
|
return jsonRequest(client, apiName, params, retval, true)
|
|
}
|
|
|
|
func jsonRequest(cli *client.Client, apiName string, params map[string]string, retval interface{}, debug bool) error {
|
|
op := &request.Operation{
|
|
Name: apiName,
|
|
HTTPMethod: "POST",
|
|
HTTPPath: "/",
|
|
Paginator: &request.Paginator{
|
|
InputTokens: []string{"NextToken"},
|
|
OutputTokens: []string{"NextToken"},
|
|
LimitToken: "MaxResults",
|
|
TruncationToken: "",
|
|
},
|
|
}
|
|
|
|
req := cli.NewRequest(op, params, retval)
|
|
err := req.Send()
|
|
if err != nil {
|
|
if e, ok := err.(awserr.RequestFailure); ok && e.StatusCode() == 404 {
|
|
return cloudprovider.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|