Skip to content

Commit

Permalink
Migrating custom action client for client v2
Browse files Browse the repository at this point in the history
  • Loading branch information
ytimocin committed Dec 20, 2022
1 parent 398850d commit c7b6c3b
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 135 deletions.
8 changes: 4 additions & 4 deletions pkg/azure/armauth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ type ArmConfig struct {
// TODO: Migrate authenticator and clients to new azure sdk - https://github.com/project-radius/radius/issues/4268
Auth autorest.Authorizer

// ClientOption is the client v2 option including new client credentials.
ClientOption clientv2.AzureClientOption
// ClientOptions is the client v2 options including new client credentials.
ClientOptions clientv2.Options
}

// GetArmConfig gets the configuration we use for managing ARM resources
Expand All @@ -47,8 +47,8 @@ func GetArmConfig() (*ArmConfig, error) {
}

return &ArmConfig{
Auth: auth,
ClientOption: clientv2.AzureClientOption{Cred: cred},
Auth: auth,
ClientOptions: clientv2.Options{Cred: cred},
}, nil
}

Expand Down
41 changes: 35 additions & 6 deletions pkg/azure/clientv2/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ package clientv2
import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
armruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
)

var defaultClientOptions = &arm.ClientOptions{
Expand All @@ -20,20 +23,46 @@ var defaultClientOptions = &arm.ClientOptions{
},
}

// AzureClientOption represents the client option for azure sdk client including authentication.
type AzureClientOption struct {
// Options represents the client option for azure sdk client including authentication.
type Options struct {
// Cred represents a credential for OAuth token.
Cred azcore.TokenCredential

BaseURI string
}

// NewFederatedIdentityClient creates new federated identity client.
func NewFederatedIdentityClient(subscriptionID string, option *AzureClientOption) (*armmsi.FederatedIdentityCredentialsClient, error) {
func NewFederatedIdentityClient(subscriptionID string, options *Options) (*armmsi.FederatedIdentityCredentialsClient, error) {
// TODO: Add LRU cache to maintain the clients.
return armmsi.NewFederatedIdentityCredentialsClient(subscriptionID, option.Cred, defaultClientOptions)
return armmsi.NewFederatedIdentityCredentialsClient(subscriptionID, options.Cred, defaultClientOptions)
}

// NewUserAssignedIdentityClient creates new user assigned managed identity client.
func NewUserAssignedIdentityClient(subscriptionID string, option *AzureClientOption) (*armmsi.UserAssignedIdentitiesClient, error) {
func NewUserAssignedIdentityClient(subscriptionID string, options *Options) (*armmsi.UserAssignedIdentitiesClient, error) {
// TODO: Add LRU cache to maintain the clients.
return armmsi.NewUserAssignedIdentitiesClient(subscriptionID, option.Cred, defaultClientOptions)
return armmsi.NewUserAssignedIdentitiesClient(subscriptionID, options.Cred, defaultClientOptions)
}

// NewCustomActionClient creates an instance of the CustomActionClient.
func NewCustomActionClient(subscriptionID string, options *Options) (*CustomActionClient, error) {
baseURI := DefaultBaseURI
if options.BaseURI != "" {
baseURI = options.BaseURI
}

client, err := armresources.NewClient(subscriptionID, options.Cred, defaultClientOptions)
if err != nil {
return nil, err
}

pipeline, err := armruntime.NewPipeline(ModuleName, ModuleVersion, options.Cred, runtime.PipelineOptions{}, defaultClientOptions)
if err != nil {
return nil, err
}

return &CustomActionClient{
client: client,
pipeline: &pipeline,
baseURI: baseURI,
}, nil
}
9 changes: 2 additions & 7 deletions pkg/azure/clientv2/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,13 @@ import (
"context"
"fmt"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
)

// GetResourceGroupLocation returns the location of the resource group.
func GetResourceGroupLocation(ctx context.Context, credential azcore.TokenCredential, subscriptionID string, resourceGroupName string) (*string, error) {
client, err := armresources.NewResourceGroupsClient(subscriptionID, credential, &arm.ClientOptions{})
if err != nil {
return nil, err
}

func GetResourceGroupLocation(ctx context.Context, subscriptionID string, resourceGroupName string, options *Options) (*string, error) {
client, err := armresources.NewResourceGroupsClient(subscriptionID, options.Cred, &arm.ClientOptions{})
if err != nil {
return nil, err
}
Expand Down
115 changes: 31 additions & 84 deletions pkg/azure/clientv2/customaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,136 +7,83 @@ package clientv2

import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
armruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/project-radius/radius/pkg/ucp/resources"
)

type CustomActionClient struct {
*BaseClient
// ClientCustomActionResponse is the response we get from invoking a custom action.
type ClientCustomActionResponse struct {
// Body is the Custom Action response body.
Body map[string]any
}

// NewCustomActionClient creates an instance of the CustomActionClient with the default Base URI.
func NewCustomActionClient(subscriptionID string, credential azcore.TokenCredential) (*BaseClient, error) {
client, err := NewCustomActionClientWithBaseURI(DefaultBaseURI, subscriptionID, credential)
if err != nil {
return nil, err
}

return client, err
// CustomActionClient is the client to invoke custom actions on Azure resources.
// Ex: listSecrets on a MongoDatabase.
type CustomActionClient struct {
client *armresources.Client
pipeline *runtime.Pipeline
baseURI string
}

// NewCustomActionClientWithBaseURI creates an instance of the CustomActionClient with a Base URI.
func NewCustomActionClientWithBaseURI(baseURI string, subscriptionID string, credential azcore.TokenCredential) (*BaseClient, error) {
options := &arm.ClientOptions{
ClientOptions: azcore.ClientOptions{
Cloud: cloud.Configuration{
Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
cloud.ResourceManager: {
Endpoint: baseURI,
},
},
},
},
}

client, err := armresources.NewClient(subscriptionID, credential, options)
// InvokeCustomAction invokes a custom action on the given resource.
func (client *CustomActionClient) InvokeCustomAction(ctx context.Context, resourceID, apiVersion, action string) (*ClientCustomActionResponse, error) {
req, err := client.customActionCreateRequest(ctx, resourceID, apiVersion, action)
if err != nil {
return nil, err
}

pipeline, err := armruntime.NewPipeline(moduleName, moduleVersion, credential, runtime.PipelineOptions{}, options)
resp, err := client.pipeline.Do(req)
if err != nil {
return nil, err
}

return &BaseClient{
Client: client,
Pipeline: &pipeline,
BaseURI: baseURI,
}, nil
}

type ClientCustomActionResponse struct {
armresources.GenericResource
}

type ClientBeginCustomActionOptions struct {
resourceID string
action string
apiVersion string
}

// New creates an instance of the CustomActionClient with the default Base URI.
func NewCustomActionRequestOptions(resourceID, action, apiVersion string) *ClientBeginCustomActionOptions {
// FIXME: This is to validate the resourceID.
_, err := resources.ParseResource(resourceID)
if err != nil {
return nil
}

return &ClientBeginCustomActionOptions{
resourceID: resourceID,
action: action,
apiVersion: apiVersion,
if !runtime.HasStatusCode(resp, http.StatusOK, http.StatusAccepted, http.StatusNoContent) {
return nil, runtime.NewResponseError(resp)
}
}

func (client *CustomActionClient) BeginCustomAction(ctx context.Context, opts *ClientBeginCustomActionOptions) (*runtime.Poller[ClientCustomActionResponse], error) {
resp, err := client.customAction(ctx, opts)
body := map[string]any{}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
return nil, err
}

// FIXME: Is this the right way?
return runtime.NewPoller[ClientCustomActionResponse](resp, *client.Pipeline, nil)
return &ClientCustomActionResponse{
Body: body,
}, nil
}

func (client *CustomActionClient) customAction(ctx context.Context, opts *ClientBeginCustomActionOptions) (*http.Response, error) {
req, err := client.customActionCreateRequest(ctx, opts)
func (client *CustomActionClient) customActionCreateRequest(ctx context.Context, resourceID, apiVersion, action string) (*policy.Request, error) {
_, err := resources.ParseResource(resourceID)
if err != nil {
return nil, err
}

resp, err := client.Pipeline.Do(req)
if err != nil {
return nil, err
}
if !runtime.HasStatusCode(resp, http.StatusAccepted, http.StatusNoContent) {
return nil, runtime.NewResponseError(resp)
}
return resp, nil
}
urlPath := "{resourceID}/{action}"

func (client *CustomActionClient) customActionCreateRequest(ctx context.Context, opts *ClientBeginCustomActionOptions) (*policy.Request, error) {
urlPath := "/{resourceID}/{action}"
if opts.resourceID == "" {
if resourceID == "" {
return nil, errors.New("resourceID cannot be empty")
}
urlPath = strings.ReplaceAll(urlPath, "{resourceID}", url.PathEscape(opts.resourceID))
urlPath = strings.ReplaceAll(urlPath, "{resourceID}", url.PathEscape(resourceID))

if opts.action == "" {
if action == "" {
return nil, errors.New("action cannot be empty")
}
urlPath = strings.ReplaceAll(urlPath, "{action}", url.PathEscape(opts.action))
urlPath = strings.ReplaceAll(urlPath, "{action}", url.PathEscape(action))

// FIXME: Is joining BaseURI and URLPath going to give us a wrong URL?
req, err := runtime.NewRequest(ctx, http.MethodPost, runtime.JoinPaths(client.BaseURI, urlPath))
req, err := runtime.NewRequest(ctx, http.MethodPost, runtime.JoinPaths(client.baseURI, urlPath))
if err != nil {
return nil, err
}
reqQP := req.Raw().URL.Query()
reqQP.Set("api-version", opts.apiVersion)
reqQP.Set("api-version", apiVersion)
req.Raw().URL.RawQuery = reqQP.Encode()
req.Raw().Header["Accept"] = []string{"application/json"}
return req, runtime.MarshalAsJSON(req, nil)
Expand Down
16 changes: 7 additions & 9 deletions pkg/azure/clientv2/resourcedeploymentclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func NewDeploymentsClientWithBaseURI(credential azcore.TokenCredential, subscrip
Cloud: cloud.Configuration{
Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
cloud.ResourceManager: {
Audience: "https://management.core.windows.net",
Endpoint: baseURI,
},
},
Expand All @@ -122,7 +123,7 @@ func NewDeploymentsClientWithBaseURI(credential azcore.TokenCredential, subscrip
return nil, err
}

pipeline, err := armruntime.NewPipeline(moduleName, moduleVersion, credential, runtime.PipelineOptions{}, options)
pipeline, err := armruntime.NewPipeline(ModuleName, ModuleVersion, credential, runtime.PipelineOptions{}, options)
if err != nil {
return nil, err
}
Expand All @@ -135,22 +136,19 @@ func NewDeploymentsClientWithBaseURI(credential azcore.TokenCredential, subscrip
}

type ClientBeginCreateOrUpdateOptions struct {
resourceID string
resumeToken string
apiVersion string
resourceID string
apiVersion string
}

func NewClientBeginCreateOrUpdateOptions(resourceID, resumeToken, apiVersion string) *ClientBeginCreateOrUpdateOptions {
// FIXME: This is to validate the resourceID.
func NewClientBeginCreateOrUpdateOptions(resourceID, apiVersion string) *ClientBeginCreateOrUpdateOptions {
_, err := resources.ParseResource(resourceID)
if err != nil {
return nil
}

return &ClientBeginCreateOrUpdateOptions{
resourceID: resourceID,
resumeToken: resumeToken,
apiVersion: apiVersion,
resourceID: resourceID,
apiVersion: apiVersion,
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/azure/clientv2/resourcedeploymentoperationsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func NewResourceDeploymentOperationsClientWithBaseURI(cred azcore.TokenCredentia
Cloud: cloud.Configuration{
Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
cloud.ResourceManager: {
Audience: "https://management.core.windows.net",
Endpoint: baseURI,
},
},
Expand Down
17 changes: 2 additions & 15 deletions pkg/azure/clientv2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,8 @@

package clientv2

import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
)

const (
DefaultBaseURI = "https://management.azure.com"

// FIXM: Any ideas for moduleName and moduleVersion?
moduleName = "radius"
moduleVersion = "public-preview"
ModuleName = "radius"
ModuleVersion = "public-preview"
)

type BaseClient struct {
Client *armresources.Client
Pipeline *runtime.Pipeline
BaseURI string
}
6 changes: 4 additions & 2 deletions pkg/corerp/handlers/azure_federatedidentity.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (handler *azureFederatedIdentityHandler) Put(ctx context.Context, options *
subID := rID.FindScope(resources.SubscriptionsSegment)
rgName := rID.FindScope(resources.ResourceGroupsSegment)

client, err := clientv2.NewFederatedIdentityClient(subID, &handler.arm.ClientOption)
client, err := clientv2.NewFederatedIdentityClient(subID, &handler.arm.ClientOptions)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -179,7 +179,9 @@ func (handler *azureFederatedIdentityHandler) Delete(ctx context.Context, option
return err
}

client, err := clientv2.NewFederatedIdentityClient(rID.FindScope(resources.SubscriptionsSegment), &handler.arm.ClientOption)
subscriptionID := rID.FindScope(resources.SubscriptionsSegment)

client, err := clientv2.NewFederatedIdentityClient(subscriptionID, &handler.arm.ClientOptions)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit c7b6c3b

Please sign in to comment.