diff --git a/go.mod b/go.mod index 6af6961aae..0820a249d1 100644 --- a/go.mod +++ b/go.mod @@ -89,7 +89,7 @@ require ( k8s.io/cluster-bootstrap v0.19.3 k8s.io/cri-api v0.22.17 moul.io/http2curl/v2 v2.3.0 - yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240321094619-9614dc43aa9b + yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240328114957-5bdec2c9d919 yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401 yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 diff --git a/go.sum b/go.sum index bed3c2e50d..c22a182cce 100644 --- a/go.sum +++ b/go.sum @@ -1210,8 +1210,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240321094619-9614dc43aa9b h1:M6R0Rp/zOJEAREMTVzuiPXEf6e25Ozh71JbZ4UuphyE= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240321094619-9614dc43aa9b/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240328114957-5bdec2c9d919 h1:mFqAWZNc6oUttUhvqxYpXUtdTV5LiZPdhfp5X3hx+ds= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240328114957-5bdec2c9d919/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug= yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws= yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634= diff --git a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go b/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go deleted file mode 100644 index 7a0b9ed102..0000000000 --- a/vendor/golang.org/x/oauth2/clientcredentials/clientcredentials.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package clientcredentials implements the OAuth2.0 "client credentials" token flow, -// also known as the "two-legged OAuth 2.0". -// -// This should be used when the client is acting on its own behalf or when the client -// is the resource owner. It may also be used when requesting access to protected -// resources based on an authorization previously arranged with the authorization -// server. -// -// See https://tools.ietf.org/html/rfc6749#section-4.4 -package clientcredentials // import "golang.org/x/oauth2/clientcredentials" - -import ( - "context" - "fmt" - "net/http" - "net/url" - "strings" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/internal" -) - -// Config describes a 2-legged OAuth2 flow, with both the -// client application information and the server's endpoint URLs. -type Config struct { - // ClientID is the application's ID. - ClientID string - - // ClientSecret is the application's secret. - ClientSecret string - - // TokenURL is the resource server's token endpoint - // URL. This is a constant specific to each server. - TokenURL string - - // Scope specifies optional requested permissions. - Scopes []string - - // EndpointParams specifies additional parameters for requests to the token endpoint. - EndpointParams url.Values - - // AuthStyle optionally specifies how the endpoint wants the - // client ID & client secret sent. The zero value means to - // auto-detect. - AuthStyle oauth2.AuthStyle -} - -// Token uses client credentials to retrieve a token. -// -// The provided context optionally controls which HTTP client is used. See the oauth2.HTTPClient variable. -func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) { - return c.TokenSource(ctx).Token() -} - -// Client returns an HTTP client using the provided token. -// The token will auto-refresh as necessary. -// -// The provided context optionally controls which HTTP client -// is returned. See the oauth2.HTTPClient variable. -// -// The returned Client and its Transport should not be modified. -func (c *Config) Client(ctx context.Context) *http.Client { - return oauth2.NewClient(ctx, c.TokenSource(ctx)) -} - -// TokenSource returns a TokenSource that returns t until t expires, -// automatically refreshing it as necessary using the provided context and the -// client ID and client secret. -// -// Most users will use Config.Client instead. -func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { - source := &tokenSource{ - ctx: ctx, - conf: c, - } - return oauth2.ReuseTokenSource(nil, source) -} - -type tokenSource struct { - ctx context.Context - conf *Config -} - -// Token refreshes the token by using a new client credentials request. -// tokens received this way do not include a refresh token -func (c *tokenSource) Token() (*oauth2.Token, error) { - v := url.Values{ - "grant_type": {"client_credentials"}, - } - if len(c.conf.Scopes) > 0 { - v.Set("scope", strings.Join(c.conf.Scopes, " ")) - } - for k, p := range c.conf.EndpointParams { - // Allow grant_type to be overridden to allow interoperability with - // non-compliant implementations. - if _, ok := v[k]; ok && k != "grant_type" { - return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k) - } - v[k] = p - } - - tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v, internal.AuthStyle(c.conf.AuthStyle)) - if err != nil { - if rErr, ok := err.(*internal.RetrieveError); ok { - return nil, (*oauth2.RetrieveError)(rErr) - } - return nil, err - } - t := &oauth2.Token{ - AccessToken: tk.AccessToken, - TokenType: tk.TokenType, - RefreshToken: tk.RefreshToken, - Expiry: tk.Expiry, - } - return t.WithExtra(tk.Raw), nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5670c490fe..61c55dbfd7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1083,7 +1083,6 @@ golang.org/x/net/trace ## explicit; go 1.11 golang.org/x/oauth2 golang.org/x/oauth2/authhandler -golang.org/x/oauth2/clientcredentials golang.org/x/oauth2/google golang.org/x/oauth2/google/internal/externalaccount golang.org/x/oauth2/internal @@ -1477,7 +1476,7 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.2.0 ## explicit; go 1.12 sigs.k8s.io/yaml -# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240321094619-9614dc43aa9b +# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240328114957-5bdec2c9d919 ## explicit; go 1.18 yunion.io/x/cloudmux/pkg/apis yunion.io/x/cloudmux/pkg/apis/billing diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure.go index afa79392e3..60a94a8791 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure.go @@ -41,7 +41,6 @@ import ( azureenv "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/pkg/errors" - "golang.org/x/oauth2/clientcredentials" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -64,7 +63,6 @@ const ( type TAzureResource string var ( - GraphResource = TAzureResource("graph") DefaultResource = TAzureResource("default") LoganalyticsResource = TAzureResource("loganalytics") ) @@ -79,6 +77,9 @@ type SAzureClient struct { clientCache map[TAzureResource]*azureAuthClient lock sync.Mutex + tokenLock sync.Mutex + tokenMap map[string]*Token + httpClient *http.Client ressourceGroups []SResourceGroup @@ -89,6 +90,8 @@ type SAzureClient struct { debug bool + ctx context.Context + workspaces []SLoganalyticsWorkspace } @@ -135,6 +138,8 @@ func NewAzureClient(cfg *AzureClientConfig) (*SAzureClient, error) { AzureClientConfig: cfg, debug: cfg.debug, clientCache: map[TAzureResource]*azureAuthClient{}, + tokenMap: map[string]*Token{}, + ctx: context.Background(), } var err error client.subscriptions, err = client.ListSubscriptions() @@ -184,13 +189,6 @@ func (self *SAzureClient) getClient(resource TAzureResource) (*azureAuthClient, client.Sender = httpClient switch resource { - case GraphResource: - ret.domain = env.GraphEndpoint - conf.Resource = env.GraphEndpoint - if self.envName == "AzureChinaCloud" { - ret.domain = "https://graph.chinacloudapi.cn/" - conf.Resource = "https://graph.chinacloudapi.cn/" - } case LoganalyticsResource: ret.domain = env.ResourceIdentifiers.OperationalInsights conf.Resource = env.ResourceIdentifiers.OperationalInsights @@ -223,10 +221,6 @@ func (self *SAzureClient) getDefaultClient() (*azureAuthClient, error) { return self.getClient(DefaultResource) } -func (self *SAzureClient) getGraphClient() (*azureAuthClient, error) { - return self.getClient(GraphResource) -} - func (self *SAzureClient) getLoganalyticsClient() (*azureAuthClient, error) { return self.getClient(LoganalyticsResource) } @@ -290,18 +284,6 @@ func (self *SAzureClient) ljsonRequest(method, path string, body jsonutils.JSONO return jsonRequest(cli.client, method, cli.domain, path, body, params, self.debug) } -func (self *SAzureClient) gjsonRequest(method, path string, body jsonutils.JSONObject, params url.Values) (jsonutils.JSONObject, error) { - cli, err := self.getGraphClient() - if err != nil { - return nil, errors.Wrapf(err, "gjsonRequest") - } - if params == nil { - params = url.Values{} - } - params.Set("api-version", "1.6") - return jsonRequest(cli.client, method, cli.domain, path, body, params, self.debug) -} - func (self *SAzureClient) put(path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { params := url.Values{} params.Set("api-version", self._apiVersion(path, params)) @@ -346,55 +328,6 @@ func (self *SAzureClient) get(resourceId string, params url.Values, retVal inter return self._get(resourceId, params, retVal, true) } -func (self *SAzureClient) gcreate(resource string, body jsonutils.JSONObject, retVal interface{}) error { - path := resource - result, err := self.msGraphRequest("POST", path, body) - if err != nil { - return errors.Wrapf(err, "msGraphRequest") - } - if gotypes.IsNil(result) { - return fmt.Errorf("empty response") - } - if retVal != nil { - return result.Unmarshal(retVal) - } - return nil -} - -func (self *SAzureClient) gpatch(resource string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { - return self.gjsonRequest("PATCH", resource, body, nil) -} - -func (self *SAzureClient) glist(resource string, params url.Values, retVal interface{}) error { - if params == nil { - params = url.Values{} - } - err := self._glist(resource, params, retVal) - if err != nil { - return errors.Wrapf(err, "_glist(%s)", resource) - } - return nil -} - -func (self *SAzureClient) _glist(resource string, params url.Values, retVal interface{}) error { - path := resource - if len(params) > 0 { - path = fmt.Sprintf("%s?%s", path, params.Encode()) - } - body, err := self.msGraphRequest("GET", path, nil) - if err != nil { - return err - } - if gotypes.IsNil(body) { - return fmt.Errorf("empty response") - } - err = body.Unmarshal(retVal, "value") - if err != nil { - return errors.Wrapf(err, "body.Unmarshal") - } - return nil -} - func (self *SAzureClient) list(resource string, params url.Values, retVal interface{}) error { if params == nil { params = url.Values{} @@ -602,18 +535,6 @@ func (self *SAzureClient) del(resourceId string) error { return err } -func (self *SAzureClient) GDelete(resourceId string) error { - return self.gdel(resourceId) -} - -func (self *SAzureClient) gdel(resourceId string) error { - _, err := self.msGraphRequest("DELETE", resourceId, nil) - if err != nil { - return errors.Wrapf(err, "gdel(%s)", resourceId) - } - return nil -} - func (self *SAzureClient) perform(resourceId string, action string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { path := fmt.Sprintf("%s/%s", resourceId, action) return self.post(path, body) @@ -1128,34 +1049,3 @@ func (self *SAzureClient) SetTags(resourceId string, tags map[string]string) (js } return self.patch(path, jsonutils.Marshal(input)) } - -func (self *SAzureClient) msGraphClient() *http.Client { - conf := clientcredentials.Config{ - ClientID: self.clientId, - ClientSecret: self.clientSecret, - - TokenURL: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", self.tenantId), - Scopes: []string{"https://graph.microsoft.com/.default"}, - } - if self.envName == "AzureChinaCloud" { - conf.TokenURL = fmt.Sprintf("https://login.partner.microsoftonline.cn/%s/oauth2/v2.0/token", self.tenantId) - conf.Scopes = []string{"https://microsoftgraph.chinacloudapi.cn/.default"} - } - return conf.Client(context.TODO()) -} - -func (self *SAzureClient) msGraphRequest(method string, resource string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { - client := self.msGraphClient() - url := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s", resource) - if self.envName == "AzureChinaCloud" { - url = fmt.Sprintf("https://microsoftgraph.chinacloudapi.cn/v1.0/%s", resource) - } - req := httputils.NewJsonRequest(httputils.THttpMethod(method), url, body) - ae := AzureResponseError{} - cli := httputils.NewJsonClient(client) - _, body, err := cli.Send(context.TODO(), req, &ae, self.debug) - if err != nil { - return nil, err - } - return body, nil -} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure_v2.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure_v2.go new file mode 100644 index 0000000000..898ccb8436 --- /dev/null +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/azure_v2.go @@ -0,0 +1,192 @@ +package azure + +import ( + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/httputils" +) + +const ( + ENV_NAME_CHINA = "AzureChinaCloud" + ENV_NAME_GLOBAL = "AzurePublicCloud" + + SERVICE_MANAGEMENT = "management" + SERVICE_GRAPH = "graph" + SERVICE_AAD = "aad" +) + +var azServices = map[string]map[string]string{ + SERVICE_GRAPH: { + ENV_NAME_GLOBAL: "https://graph.microsoft.com/v1.0", + ENV_NAME_CHINA: "https://microsoftgraph.chinacloudapi.cn/v1.0", + }, + SERVICE_MANAGEMENT: { + ENV_NAME_GLOBAL: "https://management.azure.com", + ENV_NAME_CHINA: "https://management.chinacloudapi.cn", + }, + SERVICE_AAD: { + ENV_NAME_GLOBAL: "https://login.microsoftonline.com", + ENV_NAME_CHINA: "https://login.chinacloudapi.cn", + }, +} + +type Token struct { + TokenType string + ExpiresIn int64 + ExtExpiresIn int64 + ExpiresOn int64 + NotBefore int64 + Resource string + AccessToken string +} + +func (t Token) Token() string { + return fmt.Sprintf("%s %s", t.TokenType, t.AccessToken) +} + +func (t Token) isExpire() bool { + expire := time.Unix(t.NotBefore, 0) + return expire.Before(time.Now()) +} + +func (self *SAzureClient) client() *http.Client { + if self.httpClient != nil { + return self.httpClient + } + httpClient := self.cpcfg.AdaptiveTimeoutHttpClient() + transport, _ := httpClient.Transport.(*http.Transport) + httpClient.Transport = cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response) error, error) { + if self.cpcfg.ReadOnly { + if req.Method == "GET" || (req.Method == "POST" && strings.HasSuffix(req.URL.Path, "oauth2/token")) { + return nil, nil + } + return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path) + } + return nil, nil + }) + self.httpClient = httpClient + return self.httpClient +} + +func (self *SAzureClient) auth(resource string) (string, error) { + self.tokenLock.Lock() + defer self.tokenLock.Unlock() + + if token, ok := self.tokenMap[resource]; ok && !token.isExpire() { + return token.Token(), nil + } + + data := url.Values{} + data.Set("client_id", self.clientId) + data.Set("client_secret", self.clientSecret) + data.Set("grant_type", "client_credentials") + data.Set("resource", resource) + + domain := azServices[SERVICE_AAD][self.envName] + url := fmt.Sprintf("%s/%s/oauth2/token?api-version=1.0", domain, self.tenantId) + client := self.client() + resp, err := client.PostForm(url, data) + if err != nil { + return "", errors.Wrapf(err, "auth") + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", errors.Wrapf(err, "read body") + } + obj, err := jsonutils.Parse(body) + if err != nil { + return "", errors.Wrapf(err, "parse body %s", string(body)) + } + if obj.Contains("error") { + return "", errors.Errorf(string(body)) + } + token := &Token{} + err = obj.Unmarshal(token) + if err != nil { + return "", errors.Wrapf(err, "unmarshal token") + } + self.tokenMap[resource] = token + return token.Token(), nil +} + +func (self *SAzureClient) Do(req *http.Request) (*http.Response, error) { + resource := fmt.Sprintf("https://%s", req.Host) + token, err := self.auth(resource) + if err != nil { + return nil, errors.Wrapf(err, "auth") + } + req.Header.Set("Authorization", token) + return self.client().Do(req) +} + +func (self *SAzureClient) list_v2(resource, apiVersion string, params url.Values) (jsonutils.JSONObject, error) { + return self._list_v2(SERVICE_MANAGEMENT, resource, apiVersion, params) +} + +func (self *SAzureClient) _list_v2(service string, resource, apiVersion string, params url.Values) (jsonutils.JSONObject, error) { + if params == nil { + params = url.Values{} + } + if len(apiVersion) > 0 { + params.Set("api-version", apiVersion) + } + + domain := azServices[service][self.envName] + url := fmt.Sprintf("%s/%s", domain, resource) + if len(params) > 0 { + url += fmt.Sprintf("?%s", params.Encode()) + } + _, resp, err := httputils.JSONRequest(self, self.ctx, httputils.GET, url, nil, nil, self.debug) + return resp, err +} + +func (self *SAzureClient) post_v2(resource, apiVersion string, body map[string]interface{}) (jsonutils.JSONObject, error) { + return self._post_v2("", resource, apiVersion, body) +} + +func (self *SAzureClient) _post_v2(service string, resource, apiVersion string, body map[string]interface{}) (jsonutils.JSONObject, error) { + domain := azServices[service][self.envName] + url := fmt.Sprintf("%s/%s", domain, resource) + if len(apiVersion) > 0 { + url += fmt.Sprintf("?api-version=%s", apiVersion) + } + _, resp, err := httputils.JSONRequest(self, self.ctx, httputils.POST, url, nil, jsonutils.Marshal(body), self.debug) + return resp, err +} + +func (self *SAzureClient) delete_v2(resource, apiVersion string, body map[string]interface{}) (jsonutils.JSONObject, error) { + return self._delete_v2("", resource, apiVersion) +} + +func (self *SAzureClient) _delete_v2(service string, resource, apiVersion string) (jsonutils.JSONObject, error) { + domain := azServices[service][self.envName] + url := fmt.Sprintf("%s/%s", domain, resource) + if len(apiVersion) > 0 { + url += fmt.Sprintf("?api-version=%s", apiVersion) + } + _, resp, err := httputils.JSONRequest(self, self.ctx, httputils.DELETE, url, nil, nil, self.debug) + return resp, err +} + +func (self *SAzureClient) patch_v2(resource, apiVersion string, body map[string]interface{}) (jsonutils.JSONObject, error) { + return self._patch_v2("", resource, apiVersion, body) +} + +func (self *SAzureClient) _patch_v2(service string, resource, apiVersion string, body map[string]interface{}) (jsonutils.JSONObject, error) { + domain := azServices[service][self.envName] + url := fmt.Sprintf("%s/%s", domain, resource) + if len(apiVersion) > 0 { + url += fmt.Sprintf("?api-version=%s", apiVersion) + } + _, resp, err := httputils.JSONRequest(self, self.ctx, httputils.PATCH, url, nil, jsonutils.Marshal(body), self.debug) + return resp, err +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/cloudgroup.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/cloudgroup.go index 3af489033d..9c7d8e15dc 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/cloudgroup.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/cloudgroup.go @@ -19,7 +19,6 @@ import ( "net/url" "strings" - "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/pinyinutils" @@ -121,7 +120,8 @@ func (group *SCloudgroup) DetachSystemPolicy(policyId string) error { return errors.Wrapf(err, "GetRule(%s)", assignment.Properties.RoleDefinitionId) } if role.Properties.RoleName == policyId { - return group.client.gdel(assignment.Id) + _, err := group.client._delete_v2(SERVICE_GRAPH, assignment.Id, "") + return err } } return nil @@ -136,12 +136,16 @@ func (group *SCloudgroup) Delete() error { } func (self *SAzureClient) GetCloudgroups(name string) ([]SCloudgroup, error) { - groups := []SCloudgroup{} params := url.Values{} if len(name) > 0 { params.Set("$filter", fmt.Sprintf("displayName eq '%s'", name)) } - err := self.glist("groups", params, &groups) + resp, err := self._list_v2(SERVICE_GRAPH, "groups", "", params) + if err != nil { + return nil, err + } + groups := []SCloudgroup{} + err = resp.Unmarshal(&groups, "value") if err != nil { return nil, err } @@ -177,9 +181,13 @@ func (self *SAzureClient) GetICloudgroupByName(name string) (cloudprovider.IClou } func (self *SAzureClient) ListGroupMemebers(id string) ([]SClouduser, error) { - users := []SClouduser{} resource := fmt.Sprintf("groups/%s/members", id) - err := self.glist(resource, nil, &users) + resp, err := self._list_v2(SERVICE_GRAPH, resource, "", nil) + if err != nil { + return nil, err + } + users := []SClouduser{} + err = resp.Unmarshal(&users, "value") if err != nil { return nil, err } @@ -187,7 +195,8 @@ func (self *SAzureClient) ListGroupMemebers(id string) ([]SClouduser, error) { } func (self *SAzureClient) DeleteGroup(id string) error { - return self.gdel(fmt.Sprintf("groups/%s", id)) + _, err := self._delete_v2(SERVICE_GRAPH, "groups/"+id, "") + return err } func (self *SAzureClient) CreateGroup(name, desc string) (*SCloudgroup, error) { @@ -200,12 +209,16 @@ func (self *SAzureClient) CreateGroup(name, desc string) (*SCloudgroup, error) { if len(desc) > 0 { params["Description"] = desc } - group := SCloudgroup{client: self} - err := self.gcreate("groups", jsonutils.Marshal(params), &group) + resp, err := self._post_v2(SERVICE_GRAPH, "groups", "", params) if err != nil { - return nil, errors.Wrap(err, "Create") + return nil, err } - return &group, nil + group := &SCloudgroup{client: self} + err = resp.Unmarshal(group) + if err != nil { + return nil, err + } + return group, nil } func (self *SAzureClient) RemoveGroupUser(id, userName string) error { @@ -213,7 +226,9 @@ func (self *SAzureClient) RemoveGroupUser(id, userName string) error { if err != nil { return errors.Wrapf(err, "GetCloudusers(%s)", userName) } - return self.gdel(fmt.Sprintf("/groups/%s/members/%s/$ref", id, user.Id)) + resource := fmt.Sprintf("/groups/%s/members/%s/$ref", id, user.Id) + _, err = self._delete_v2(SERVICE_GRAPH, resource, "") + return err } func (self *SAzureClient) CreateICloudgroup(name, desc string) (cloudprovider.ICloudgroup, error) { @@ -230,14 +245,14 @@ func (self *SAzureClient) AddGroupUser(id, userName string) error { if err != nil { return errors.Wrapf(err, "GetCloudusers(%s)", userName) } - resource := fmt.Sprintf("groups/%s/members/$ref", id) - params := map[string]string{ + params := map[string]interface{}{ "@odata.id": fmt.Sprintf("https://graph.microsoft.com/v1.0/directoryObjects/%s", user.Id), } if self.envName == "AzureChinaCloud" { params["@odata.id"] = fmt.Sprintf("https://microsoftgraph.chinacloudapi.cn/v1.0/directoryObjects/%s", user.Id) } - err = self.gcreate(resource, jsonutils.Marshal(params), nil) + resource := fmt.Sprintf("groups/%s/members/$ref", id) + _, err = self._post_v2(SERVICE_GRAPH, resource, "", params) if err != nil && !strings.Contains(err.Error(), "One or more added object references already exist for the following modified properties") { return err } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/clouduser.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/clouduser.go index 3d33435171..193cfe4715 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/clouduser.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/clouduser.go @@ -19,9 +19,7 @@ import ( "net/url" "time" - "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" - "yunion.io/x/pkg/util/httputils" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/cloudmux/pkg/multicloud" @@ -162,7 +160,8 @@ func (user *SClouduser) DetachSystemPolicy(policyId string) error { return errors.Wrapf(err, "GetRule(%s)", assignment.Properties.RoleDefinitionId) } if role.Properties.RoleName == policyId { - return user.client.gdel(assignment.Id) + _, err := user.client._delete_v2(SERVICE_GRAPH, assignment.Id, "") + return err } } return nil @@ -200,20 +199,27 @@ func (user *SClouduser) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { func (self *SAzureClient) GetUserGroups(userId string) ([]SCloudgroup, error) { resource := fmt.Sprintf("users/%s/memberOf", userId) + resp, err := self._list_v2(SERVICE_GRAPH, resource, "", nil) + if err != nil { + return nil, err + } groups := []SCloudgroup{} - err := self.glist(resource, url.Values{}, &groups) + err = resp.Unmarshal(&groups, "value") + if err != nil { + return nil, err + } return groups, err } func (self *SAzureClient) ResetClouduserPassword(id, password string) error { - body := jsonutils.Marshal(map[string]interface{}{ + body := map[string]interface{}{ "passwordPolicies": "DisablePasswordExpiration, DisableStrongPassword", "passwordProfile": map[string]interface{}{ "password": password, }, - }) + } resource := fmt.Sprintf("%s/users/%s", self.tenantId, id) - _, err := self.gpatch(resource, body) + _, err := self._patch_v2(SERVICE_GRAPH, resource, "", body) return err } @@ -233,8 +239,11 @@ func (self *SAzureClient) GetClouduser(name string) (*SClouduser, error) { func (self *SAzureClient) GetCloudusers() ([]SClouduser, error) { users := []SClouduser{} - params := url.Values{} - err := self.glist("users", params, &users) + resp, err := self._list_v2(SERVICE_GRAPH, "users", "", url.Values{}) + if err != nil { + return nil, err + } + err = resp.Unmarshal(&users, "value") if err != nil { return nil, err } @@ -242,14 +251,14 @@ func (self *SAzureClient) GetCloudusers() ([]SClouduser, error) { } func (self *SAzureClient) DeleteClouduser(id string) error { - _, err := self.msGraphRequest(string(httputils.DELETE), "users/"+id, nil) + _, err := self._delete_v2(SERVICE_GRAPH, "users/"+id, "") return err } func (self *SAzureClient) GetICloudusers() ([]cloudprovider.IClouduser, error) { - users, err := self.ListGraphUsers() + users, err := self.GetCloudusers() if err != nil { - return nil, errors.Wrap(err, "ListGraphUsers") + return nil, errors.Wrap(err, "GetCloudusers") } ret := []cloudprovider.IClouduser{} for i := range users { @@ -293,9 +302,13 @@ type SDomain struct { func (self *SAzureClient) GetDomains() ([]SDomain, error) { domains := []SDomain{} - err := self.glist("domains", nil, &domains) + resp, err := self._list_v2(SERVICE_GRAPH, "domains", "", nil) if err != nil { - return nil, errors.Wrap(err, "glist") + return nil, errors.Wrap(err, "list domains") + } + err = resp.Unmarshal(&domains, "value") + if err != nil { + return nil, err } return domains, nil } @@ -332,27 +345,14 @@ func (self *SAzureClient) CreateClouduser(name, password string) (*SClouduser, e return nil, errors.Wrap(err, "GetDefaultDomain") } params["userPrincipalName"] = fmt.Sprintf("%s@%s", name, domain) - user := SClouduser{client: self} - resp, err := self.msGraphRequest(string(httputils.POST), "users", jsonutils.Marshal(params)) + user := &SClouduser{client: self} + resp, err := self._post_v2(SERVICE_GRAPH, "users", "", params) if err != nil { return nil, errors.Wrap(err, "Create") } - err = resp.Unmarshal(&user) + err = resp.Unmarshal(user) if err != nil { return nil, err } - return &user, nil -} - -func (self *SAzureClient) ListGraphUsers() ([]SClouduser, error) { - resp, err := self.msGraphRequest("GET", "users", nil) - if err != nil { - return nil, errors.Wrapf(err, "msGraphRequest.users") - } - users := []SClouduser{} - err = resp.Unmarshal(&users, "value") - if err != nil { - return nil, errors.Wrapf(err, "resp.Unmarshal") - } - return users, nil + return user, nil } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/enrollment_account.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/enrollment_account.go index 9440b4012a..e82dfbf391 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/enrollment_account.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/enrollment_account.go @@ -95,6 +95,14 @@ func (cli *SAzureClient) ListServicePrincipal(appId string) ([]SServicePrincipal if len(appId) > 0 { params.Set("$filter", fmt.Sprintf(`appId eq '%s'`, cli.clientId)) } + resp, err := cli._list_v2(SERVICE_GRAPH, "servicePrincipals", "", params) + if err != nil { + return nil, err + } result := []SServicePrincipal{} - return result, cli.glist("servicePrincipals", params, &result) + err = resp.Unmarshal(&result, "value") + if err != nil { + return nil, err + } + return result, nil } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/saml_provider.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/saml_provider.go index a3d6c8800c..bb27e6cdea 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/saml_provider.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/azure/saml_provider.go @@ -79,25 +79,30 @@ func (self *SAMLProvider) GetAuthUrl(apiServer string) string { } func (self *SAzureClient) ListSAMLProviders() ([]SAMLProvider, error) { - _, err := self.msGraphRequest("GET", "identityProviders", nil) + resp, err := self._list_v2(SERVICE_GRAPH, "identityProviders", "", nil) if err != nil { return nil, err } - return []SAMLProvider{}, nil + ret := []SAMLProvider{} + err = resp.Unmarshal(&ret, "value") + if err != nil { + return nil, err + } + return ret, nil } func (self *SAzureClient) InviteUser(email string) (*SClouduser, error) { - body := jsonutils.Marshal(map[string]string{ + body := map[string]interface{}{ "invitedUserEmailAddress": email, "inviteRedirectUrl": fmt.Sprintf("https://portal.azure.com/%s?login_hint=%s", self.tenantId, email), - }) - resp, err := self.msGraphRequest("POST", "invitations", body) + } + resp, err := self._post_v2(SERVICE_GRAPH, "invitations", "", body) if err != nil { - return nil, errors.Wrapf(err, "msGraphRequest.invitations") + return nil, errors.Wrapf(err, "invitations") } inviteUrl, _ := resp.GetString("inviteRedeemUrl") err = cloudprovider.Wait(time.Second*2, time.Minute, func() (bool, error) { - users, err := self.ListGraphUsers() + users, err := self.GetCloudusers() if err != nil { return false, errors.Wrapf(err, "GetCloudusers") } @@ -112,7 +117,7 @@ func (self *SAzureClient) InviteUser(email string) (*SClouduser, error) { if err != nil { return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after invite %s", email) } - users, err := self.ListGraphUsers() + users, err := self.GetCloudusers() if err != nil { return nil, errors.Wrapf(err, "GetCloudusers") } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/nutanix.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/nutanix.go index d03b5b5abb..e82dcdfedd 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/nutanix.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/nutanix.go @@ -328,6 +328,10 @@ func (self *SNutanixClient) get(res string, id string, params url.Values, retVal return nil } +func (cli *SNutanixClient) GetCloudRegionExternalIdPrefix() string { + return fmt.Sprintf("%s/%s/", CLOUD_PROVIDER_NUTANIX, cli.cpcfg.Id) +} + func (self *SNutanixClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { subAccount := cloudprovider.SSubAccount{ Id: self.GetAccountId(), diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/provider/provider.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/provider/provider.go index fce51e6134..9a51dd3c42 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/provider/provider.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/nutanix/provider/provider.go @@ -143,6 +143,10 @@ type SNutanixProvider struct { client *nutanix.SNutanixClient } +func (self *SNutanixProvider) GetCloudRegionExternalIdPrefix() string { + return self.client.GetCloudRegionExternalIdPrefix() +} + func (self *SNutanixProvider) GetSysInfo() (jsonutils.JSONObject, error) { return jsonutils.NewDict(), nil } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/provider/provider.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/provider/provider.go index adf805725e..f98176868e 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/provider/provider.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/provider/provider.go @@ -143,6 +143,10 @@ type SProxmoxProvider struct { client *proxmox.SProxmoxClient } +func (self *SProxmoxProvider) GetCloudRegionExternalIdPrefix() string { + return self.client.GetCloudRegionExternalIdPrefix() +} + func (self *SProxmoxProvider) GetSysInfo() (jsonutils.JSONObject, error) { return jsonutils.NewDict(), nil } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go index 0738ccbf2b..7af7a0328b 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/proxmox/proxmox.go @@ -20,7 +20,6 @@ import ( "crypto/tls" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http" "net/url" @@ -341,7 +340,7 @@ func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io. } defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -369,6 +368,10 @@ func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after upload") } +func (cli *SProxmoxClient) GetCloudRegionExternalIdPrefix() string { + return fmt.Sprintf("%s/%s/", CLOUD_PROVIDER_PROXMOX, cli.cpcfg.Id) +} + func (self *SProxmoxClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { subAccount := cloudprovider.SSubAccount{} subAccount.Id = self.host