Merge pull request #13922 from ioito/hotfix/qx-account-read-only

fix(region): account read only
This commit is contained in:
Zexi Li
2022-04-08 09:58:09 +08:00
committed by GitHub
32 changed files with 366 additions and 19 deletions
+1 -1
View File
@@ -153,7 +153,7 @@ require (
yunion.io/x/jsonutils v0.0.0-20220106020632-953b71a4c3a8
yunion.io/x/log v0.0.0-20201210064738-43181789dc74
yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7
yunion.io/x/pkg v0.0.0-20220227083757-28690b33ce38
yunion.io/x/pkg v0.0.0-20220406030238-39fbc60d5d4e
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
yunion.io/x/sqlchemy v0.0.0-20220405074102-7b815a97eb67
yunion.io/x/structarg v0.0.0-20220312084958-9c6c79c7d1c6
+2 -2
View File
@@ -994,8 +994,8 @@ yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7/go.mod h1:0vLkNEhlmA64HViPB
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20220227083757-28690b33ce38 h1:QV3gVskJfi1J0e5kTs4oTx8D+Mc0JFik3Qe6c1PgO90=
yunion.io/x/pkg v0.0.0-20220227083757-28690b33ce38/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20220406030238-39fbc60d5d4e h1:ovxSHuAa8hOBiZPU+ezrwqmVuCN7QTNx0sRRDGMaAkY=
yunion.io/x/pkg v0.0.0-20220406030238-39fbc60d5d4e/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v0.0.0-20220405074102-7b815a97eb67 h1:vZiiB5oc6wqEvr1UNmJPFCX3FmLznZbvoUZ90ONgAcE=
+4
View File
@@ -207,6 +207,8 @@ type CloudaccountCreateInput struct {
// swagger:ignore
SubAccounts *cloudprovider.SubAccounts
ReadOnly bool `json:"read_only"`
}
type CloudaccountShareModeInput struct {
@@ -339,6 +341,8 @@ type CloudaccountUpdateInput struct {
// 临时清除缺失的权限提示,云账号权限缺失依然会自动刷新
CleanLakeOfPermissions bool `json:"clean_lake_of_permissions"`
ReadOnly bool `json:"read_only"`
}
type CloudaccountPerformPublicInput struct {
+2
View File
@@ -138,6 +138,8 @@ type CloudproviderDetails struct {
// 子订阅品牌信息
Brand string `json:"brand"`
ReadOnly bool `json:"read_only"`
ProjectMappingResourceInfo
}
+7
View File
@@ -79,6 +79,8 @@ type SCloudaccount struct {
SAMLAuth tristate.TriState `list:"domain" default:"false"`
AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"domain" update:"domain" create:"domain_optional"`
ReadOnly bool `default:"false" create:"domain_optional" list:"domain" update:"domain"`
}
func (manager *SCloudaccountManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
@@ -395,6 +397,7 @@ func (self *SCloudaccount) syncWithICloudaccount(ctx context.Context, userCred m
self.SAMLAuth = account.SAMLAuth
self.AccountId = account.AccountId
self.AccessUrl = account.AccessUrl
self.ReadOnly = account.ReadOnly
return nil
})
if err != nil {
@@ -481,6 +484,8 @@ type SCloudDelegate struct {
Provider string
Brand string
ReadOnly bool
Options struct {
cloudprovider.SHCSOEndpoints
cloudprovider.SApsaraEndpoints
@@ -570,6 +575,8 @@ func (account *SCloudDelegate) GetProvider() (cloudprovider.ICloudProvider, erro
Secret: passwd,
ProxyFunc: proxyFunc,
ReadOnly: account.ReadOnly,
DefaultRegion: defaultRegion,
Options: options.(*jsonutils.JSONDict),
+2
View File
@@ -166,6 +166,8 @@ type ProviderConfig struct {
Account string
Secret string
ReadOnly bool
AccountId string
Options *jsonutils.JSONDict
+7 -6
View File
@@ -29,12 +29,13 @@ const (
CloudVMStatusDeploying = "deploying"
CloudVMStatusOther = "other"
ErrNotFound = errors.ErrNotFound
ErrDuplicateId = errors.ErrDuplicateId
ErrInvalidStatus = errors.ErrInvalidStatus
ErrTimeout = errors.ErrTimeout
ErrNotImplemented = errors.ErrNotImplemented
ErrNotSupported = errors.ErrNotSupported
ErrNotFound = errors.ErrNotFound
ErrDuplicateId = errors.ErrDuplicateId
ErrInvalidStatus = errors.ErrInvalidStatus
ErrTimeout = errors.ErrTimeout
ErrNotImplemented = errors.ErrNotImplemented
ErrNotSupported = errors.ErrNotSupported
ErrAccountReadOnly = errors.ErrAccountReadOnly
ErrInvalidProvider = httperrors.ErrInvalidProvider
ErrNoBalancePermission = httperrors.ErrNoBalancePermission
+35
View File
@@ -0,0 +1,35 @@
// 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 cloudprovider
import "net/http"
type transport struct {
readOnlyCheck func(req *http.Request) error
ts *http.Transport
}
func (self *transport) RoundTrip(req *http.Request) (*http.Response, error) {
err := self.readOnlyCheck(req)
if err != nil {
return nil, err
}
return self.ts.RoundTrip(req)
}
func GetReadOnlyCheckTransport(ts *http.Transport, check func(req *http.Request) error) http.RoundTripper {
ret := &transport{ts: ts, readOnlyCheck: check}
return ret
}
+4
View File
@@ -166,6 +166,8 @@ type SCloudaccount struct {
SProjectMappingResourceBase
ReadOnly bool `default:"false" create:"domain_optional" list:"domain" update:"domain"`
// 设置允许同步的账号及订阅
SubAccounts *cloudprovider.SubAccounts `nullable:"true" get:"user" create:"optional"`
@@ -991,6 +993,8 @@ func (self *SCloudaccount) getProviderInternal(ctx context.Context) (cloudprovid
DefaultRegion: defaultRegion,
ProxyFunc: self.proxyFunc(),
ReadOnly: self.ReadOnly,
UpdatePermission: self.UpdatePermission(ctx),
})
}
+3
View File
@@ -870,6 +870,8 @@ func (self *SCloudprovider) GetProvider(ctx context.Context) (cloudprovider.IClo
Secret: passwd,
ProxyFunc: account.proxyFunc(),
ReadOnly: account.ReadOnly,
DefaultRegion: defaultRegion,
Options: account.Options,
@@ -1015,6 +1017,7 @@ func (manager *SCloudproviderManager) FetchCustomizeColumns(
for i := range rows {
if account, ok := accounts[accountIds[i]]; ok {
rows[i].Cloudaccount = account.Name
rows[i].ReadOnly = account.ReadOnly
rows[i].Brand = account.Brand
ps := &rows[i].ProxySetting
+8
View File
@@ -98,6 +98,14 @@ func (this *Client) SetHttpTransportProxyFunc(proxyFunc httputils.TransportProxy
httputils.SetClientProxyFunc(this.httpconn, proxyFunc)
}
func (this *Client) GetClient() *http.Client {
return this.httpconn
}
func (this *Client) SetTransport(ts http.RoundTripper) {
this.httpconn.Transport = ts
}
func (this *Client) SetDebug(debug bool) {
this.debug = debug
}
+3
View File
@@ -121,6 +121,7 @@ type SCloudAccountCreateBaseOptions struct {
ProxySetting string `help:"proxy setting id or name" json:"proxy_setting"`
DryRun bool `help:"test create cloudaccount params"`
ShowSubAccounts bool `help:"test and show subaccount info"`
ReadOnly bool `help:"Read only account"`
}
type SVMwareCloudAccountCreateOptions struct {
@@ -560,6 +561,8 @@ type SCloudAccountUpdateBaseOptions struct {
ProxySetting string `help:"proxy setting name or id" json:"proxy_setting"`
SamlAuth string `help:"Enable or disable saml auth" choices:"true|false"`
ReadOnly *bool `help:"is account read only" negative:"no_read_only"`
CleanLakeOfPermissions bool `help:"clean lake of permissions"`
Desc string `help:"Description" json:"description" token:"desc"`
+28
View File
@@ -16,6 +16,8 @@ package aliyun
import (
"fmt"
"net/http"
"net/url"
"strings"
"time"
@@ -406,6 +408,22 @@ func (self *SAliyunClient) getSdkClient(regionId string) (*sdk.Client, error) {
regionId,
&sdk.Config{
HttpTransport: transport,
Transport: cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
params, err := url.ParseQuery(req.URL.RawQuery)
if err != nil {
return errors.Wrapf(err, "ParseQuery(%s)", req.URL.RawQuery)
}
action := params.Get("Action")
for _, prefix := range []string{"Get", "List", "Describe"} {
if strings.HasPrefix(action, prefix) {
return nil
}
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
return nil
}),
},
&credentials.BaseCredential{
AccessKeyId: self.accessKey,
@@ -512,6 +530,16 @@ func (client *SAliyunClient) getOssClientByEndpoint(endpoint string) (*oss.Clien
// https_proxy setting
// oss use no timeout client so as to send/download large files
httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
transport, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if client.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.RawPath)
}
return nil
})
cliOpts := []oss.ClientOption{
oss.HTTPClient(httpClient),
}
+28
View File
@@ -17,6 +17,8 @@ package apsara
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"strings"
"time"
@@ -277,6 +279,22 @@ func (self *SApsaraClient) getDefaultClient(regionId string) (*sdk.Client, error
regionId,
&sdk.Config{
HttpTransport: transport,
Transport: cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
params, err := url.ParseQuery(req.URL.RawQuery)
if err != nil {
return errors.Wrapf(err, "ParseQuery(%s)", req.URL.RawQuery)
}
action := params.Get("Action")
for _, prefix := range []string{"Get", "List", "Describe"} {
if strings.HasPrefix(action, prefix) {
return nil
}
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
return nil
}),
},
&credentials.BaseCredential{
AccessKeyId: self.accessKey,
@@ -347,6 +365,16 @@ func (client *SApsaraClient) getOssClient(regionId string) (*oss.Client, error)
// https_proxy setting
// oss use no timeout client so as to send/download large files
httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
transport, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if client.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
cliOpts := []oss.ClientOption{
oss.HTTPClient(httpClient),
}
+33
View File
@@ -15,7 +15,11 @@
package aws
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
@@ -239,6 +243,35 @@ func (client *SAwsClient) getAwsSession(regionId string, assumeRole bool) (*sess
return sess, nil
}
httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
transport, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if client.cpcfg.ReadOnly {
if req.ContentLength > 0 {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return errors.Wrapf(err, "ioutil.ReadAll")
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
params, err := url.ParseQuery(string(body))
if err != nil {
return errors.Wrapf(err, "ParseQuery(%s)", string(body))
}
action := params.Get("Action")
for _, prefix := range []string{"Get", "List", "Describe"} {
if strings.HasPrefix(action, prefix) {
return nil
}
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
// s3
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
s, err := session.NewSession(&sdk.Config{
Region: sdk.String(regionId),
Credentials: credentials.NewStaticCredentials(
+10
View File
@@ -158,6 +158,16 @@ func (self *SAzureClient) getClient(resource TAzureResource) (*autorest.Client,
}
httpClient := self.cpcfg.AdaptiveTimeoutHttpClient()
transport, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(transport, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
client.Sender = httpClient
self.env = env
+7
View File
@@ -211,6 +211,13 @@ func (e sBingoError) Error() string {
}
func (self *SBingoCloudClient) invoke(action string, params map[string]string) (jsonutils.JSONObject, error) {
if self.cpcfg.ReadOnly {
for _, prefix := range []string{"Get", "List", "Describe"} {
if strings.HasPrefix(action, prefix) {
return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
}
}
var encode = func(k, v string) string {
d := url.Values{}
d.Set(k, v)
+15
View File
@@ -16,6 +16,7 @@ package cloudpods
import (
"context"
"net/http"
"strings"
"yunion.io/x/jsonutils"
@@ -93,6 +94,20 @@ type SCloudpodsClient struct {
func (self *SCloudpodsClient) auth() error {
client := mcclient.NewClient(self.authURL, 0, self.debug, true, "", "")
client.SetHttpTransportProxyFunc(self.cpcfg.ProxyFunc)
ts, _ := client.GetClient().Transport.(*http.Transport)
client.SetTransport(cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
// 认证
if req.Method == "POST" && req.URL.Path == "/v3/auth/tokens" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
}))
token, err := client.AuthenticateByAccessKey(self.accessKey, self.accessSecret, "cli")
if err != nil {
if errors.Cause(err) == httperrors.ErrUnauthorized {
+11
View File
@@ -29,6 +29,7 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
@@ -89,6 +90,16 @@ type SCtyunClient struct {
func NewSCtyunClient(cfg *CtyunClientConfig) (*SCtyunClient, error) {
httpClient := cfg.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if cfg.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
client := &SCtyunClient{
CtyunClientConfig: cfg,
httpClient: httpClient,
+11
View File
@@ -139,6 +139,17 @@ func NewGoogleClient(cfg *GoogleClientConfig) (*SGoogleClient, error) {
}
httpClient := cfg.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if cfg.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
ctx := context.Background()
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
+11
View File
@@ -16,6 +16,7 @@ package hcso
import (
"fmt"
"net/http"
"strings"
"time"
@@ -166,6 +167,16 @@ func (self *SHuaweiClient) newGeneralAPIClient() (*client.Client, error) {
}
httpClient := self.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
cli.SetHttpClient(httpClient)
return cli, nil
+13
View File
@@ -16,6 +16,7 @@ package hcso
import (
"fmt"
"net/http"
"strings"
"time"
@@ -102,6 +103,18 @@ func (self *SRegion) getOBSClient() (*obs.ObsClient, error) {
return nil, err
}
client := obsClient.GetClient()
ts, _ := client.Transport.(*http.Transport)
client.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if self.client.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
self.obsClient = obsClient
}
+11
View File
@@ -16,6 +16,7 @@ package huawei
import (
"fmt"
"net/http"
"strings"
"time"
@@ -171,6 +172,16 @@ func (self *SHuaweiClient) newGeneralAPIClient() (*client.Client, error) {
}
httpClient := self.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if self.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
cli.SetHttpClient(httpClient)
return cli, nil
+4
View File
@@ -41,6 +41,10 @@ type ObsClient struct {
httpClient *http.Client
}
func (self *ObsClient) GetClient() *http.Client {
return self.httpClient
}
func New(ak, sk, endpoint string, configurers ...configurer) (*ObsClient, error) {
conf := &config{securityProvider: &securityProvider{ak: ak, sk: sk}, endpoint: endpoint}
conf.maxRetryCount = -1
+13
View File
@@ -16,6 +16,7 @@ package huawei
import (
"fmt"
"net/http"
"strings"
"time"
@@ -99,6 +100,18 @@ func (self *SRegion) getOBSClient() (*obs.ObsClient, error) {
return nil, err
}
client := obsClient.GetClient()
ts, _ := client.Transport.(*http.Transport)
client.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if self.client.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
self.obsClient = obsClient
}
+12
View File
@@ -131,6 +131,18 @@ func (cli *SNutanixClient) getDefaultClient(timeout time.Duration) *http.Client
return nil, nil
}
httputils.SetClientProxyFunc(client, proxy)
ts, _ := client.Transport.(*http.Transport)
client.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if cli.cpcfg.ReadOnly {
if req.Method == "GET" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
return client
}
+16
View File
@@ -423,6 +423,22 @@ func (cli *SOpenStackClient) getDefaultSession(regionName string) *mcclient.Clie
func (cli *SOpenStackClient) getDefaultClient() *mcclient.Client {
client := mcclient.NewClient(cli.authURL, 5, cli.debug, false, "", "")
client.SetHttpTransportProxyFunc(cli.cpcfg.ProxyFunc)
_client := client.GetClient()
ts, _ := _client.Transport.(*http.Transport)
_client.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if cli.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
// 认证
if req.Method == "POST" && strings.HasSuffix(req.URL.Path, "auth/tokens") {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
return client
}
+36 -4
View File
@@ -15,9 +15,11 @@
package qcloud
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -608,7 +610,28 @@ func (client *SQcloudClient) getSdkClient(regionId string) (*common.Client, erro
return nil, err
}
httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
cli.WithHttpTransport(httpClient.Transport)
ts, _ := httpClient.Transport.(*http.Transport)
cli.WithHttpTransport(cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if client.cpcfg.ReadOnly {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return errors.Wrapf(err, "ioutil.ReadAll")
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
params, err := url.ParseQuery(string(body))
if err != nil {
return errors.Wrapf(err, "ParseQuery(%s)", string(body))
}
action := params.Get("Action")
for _, prefix := range []string{"Get", "List", "Describe"} {
if strings.HasPrefix(action, prefix) {
return nil
}
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
return nil
}))
return cli, nil
}
@@ -870,6 +893,9 @@ func (client *SQcloudClient) getCosClient(bucket *SBucket) (*cos.Client, error)
BucketURL: u,
}
}
ts := &http.Transport{
Proxy: client.cpcfg.ProxyFunc,
}
cosClient := cos.NewClient(
baseUrl,
&http.Client{
@@ -881,9 +907,15 @@ func (client *SQcloudClient) getCosClient(bucket *SBucket) (*cos.Client, error)
RequestBody: client.debug,
ResponseHeader: client.debug,
ResponseBody: client.debug,
Transport: &http.Transport{
Proxy: client.cpcfg.ProxyFunc,
},
Transport: cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if client.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s", req.Method, req.URL.Path)
}
return nil
}),
},
},
},
+6
View File
@@ -19,6 +19,9 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
func unmarshalResult(resp jsonutils.JSONObject, respErr error, resultKey string, result interface{}) error {
@@ -76,6 +79,9 @@ func doListPart(client *SUcloudClient, action string, params SParams, resultKey
// 执行操作
func DoAction(client *SUcloudClient, action string, params SParams, resultKey string, result interface{}) error {
if client.cpcfg.ReadOnly {
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
}
params.SetAction(action)
resp, err := jsonRequest(client, params)
return unmarshalResult(resp, err, resultKey, result)
+15
View File
@@ -106,6 +106,21 @@ func getSignUrl(uri string) (string, error) {
func NewZStackClient(cfg *ZstackClientConfig) (*SZStackClient, error) {
httpClient := cfg.cpcfg.AdaptiveTimeoutHttpClient()
ts, _ := httpClient.Transport.(*http.Transport)
httpClient.Transport = cloudprovider.GetReadOnlyCheckTransport(ts, func(req *http.Request) error {
if cfg.cpcfg.ReadOnly {
if req.Method == "GET" || req.Method == "HEAD" {
return nil
}
// 认证
if req.Method == "PUT" && req.URL.Path == "/zstack/v1/accounts/login" {
return nil
}
return errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
}
return nil
})
cli := &SZStackClient{
ZstackClientConfig: cfg,
httpClient: httpClient,
+1 -1
View File
@@ -1231,7 +1231,7 @@ yunion.io/x/log/hooks
yunion.io/x/ovsdb/cli_util
yunion.io/x/ovsdb/schema/ovn_nb
yunion.io/x/ovsdb/types
# yunion.io/x/pkg v0.0.0-20220227083757-28690b33ce38
# yunion.io/x/pkg v0.0.0-20220406030238-39fbc60d5d4e
yunion.io/x/pkg/errors
yunion.io/x/pkg/gotypes
yunion.io/x/pkg/prettytable
+7 -5
View File
@@ -10,13 +10,15 @@ const (
ErrEOF = Error("EOFError")
ErrNetwork = Error("NetworkError")
ErrConnectRefused = Error("ConnectRefusedError")
ErrConnectReset = Error("ConnectResetError")
ErrTimeout = Error("TimeoutError")
ErrNotFound = Error("NotFoundError")
ErrDuplicateId = Error("DuplicateIdError")
ErrInvalidStatus = Error("InvalidStatusError")
ErrNotImplemented = Error("NotImplementedError")
ErrNotSupported = Error("NotSupportedError")
ErrNotFound = Error("NotFoundError")
ErrDuplicateId = Error("DuplicateIdError")
ErrInvalidStatus = Error("InvalidStatusError")
ErrNotImplemented = Error("NotImplementedError")
ErrNotSupported = Error("NotSupportedError")
ErrAccountReadOnly = Error("AccountReadOnlyError")
ErrAggregate = Error("AggregateError")
)