Skip to content

Commit

Permalink
Add async disk service and generate code
Browse files Browse the repository at this point in the history
  • Loading branch information
Jont828 committed Nov 8, 2021
1 parent fef4e34 commit ca63d3f
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 67 deletions.
2 changes: 2 additions & 0 deletions api/v1beta1/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ const (
AvailabilitySetReadyCondition clusterv1.ConditionType = "AvailabilitySetReady"
// RoleAssignmentReadyCondition means the role assignment exists and is ready to be used.
RoleAssignmentReadyCondition clusterv1.ConditionType = "RoleAssignmentReady"
// DisksReadyCondition means the disks exist and are ready to be used.
DisksReadyCondition clusterv1.ConditionType = "DisksReady"

// CreatingReason means the resource is being created.
CreatingReason = "Creating"
Expand Down
14 changes: 8 additions & 6 deletions azure/scope/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (

infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-azure/azure"
"sigs.k8s.io/cluster-api-provider-azure/azure/services/disks"
"sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus"
"sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachines"
"sigs.k8s.io/cluster-api-provider-azure/util/futures"
Expand Down Expand Up @@ -252,16 +253,17 @@ func (m *MachineScope) NICIDs() []string {
}

// DiskSpecs returns the disk specs.
func (m *MachineScope) DiskSpecs() []azure.DiskSpec {
disks := make([]azure.DiskSpec, 1+len(m.AzureMachine.Spec.DataDisks))
disks[0] = azure.DiskSpec{
Name: azure.GenerateOSDiskName(m.Name()),
func (m *MachineScope) DiskSpecs() []azure.ResourceSpecGetter {
diskSpecs := make([]azure.ResourceSpecGetter, 1+len(m.AzureMachine.Spec.DataDisks))
diskSpecs[0] = &disks.DiskSpec{
Name: azure.GenerateOSDiskName(m.Name()),
ResourceGroup: m.ResourceGroup(),
}

for i, dd := range m.AzureMachine.Spec.DataDisks {
disks[i+1] = azure.DiskSpec{Name: azure.GenerateDataDiskName(m.Name(), dd.NameSuffix)}
diskSpecs[i+1] = &disks.DiskSpec{Name: azure.GenerateDataDiskName(m.Name(), dd.NameSuffix)}
}
return disks
return diskSpecs
}

// RoleAssignmentSpecs returns the role assignment specs.
Expand Down
68 changes: 50 additions & 18 deletions azure/services/disks/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,48 +21,80 @@ import (

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute"
"github.com/Azure/go-autorest/autorest"
azureautorest "github.com/Azure/go-autorest/autorest/azure"
"github.com/pkg/errors"

"sigs.k8s.io/cluster-api-provider-azure/azure"
"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
"sigs.k8s.io/cluster-api-provider-azure/util/tele"
)

// Client wraps go-sdk.
type client interface {
Delete(context.Context, string, string) error
type Client interface {
DeleteAsync(context.Context, azure.ResourceSpecGetter) (azureautorest.FutureAPI, error)
Result(context.Context, azureautorest.FutureAPI, string) (interface{}, error)
IsDone(context.Context, azureautorest.FutureAPI) (bool, error)
}

// AzureClient contains the Azure go-sdk Client.
type azureClient struct {
type AzureClient struct {
disks compute.DisksClient
}

var _ client = (*azureClient)(nil)
var _ Client = (*AzureClient)(nil)

// newClient creates a new VM client from subscription ID.
func newClient(auth azure.Authorizer) *azureClient {
c := newDisksClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer())
return &azureClient{c}
// NewClient creates a new VM Client from subscription ID.
func NewClient(auth azure.Authorizer) *AzureClient {
c := NewDisksClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer())
return &AzureClient{c}
}

// newDisksClient creates a new disks client from subscription ID.
func newDisksClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) compute.DisksClient {
// NewDisksClient creates a new disks Client from subscription ID.
func NewDisksClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) compute.DisksClient {
disksClient := compute.NewDisksClientWithBaseURI(baseURI, subscriptionID)
azure.SetAutoRestClientDefaults(&disksClient.Client, authorizer)
return disksClient
}

// Delete removes the disk client.
func (ac *azureClient) Delete(ctx context.Context, resourceGroupName, name string) error {
ctx, _, done := tele.StartSpanWithLogger(ctx, "disks.AzureClient.Delete")
defer done()
// DeleteAsync deletes a route table asynchronously. DeleteAsync sends a DELETE
// request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing
// progress of the operation.
func (ac *AzureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (azureautorest.FutureAPI, error) {
ctx, span := tele.Tracer().Start(ctx, "disks.AzureClient.Delete")
defer span.End()

future, err := ac.disks.Delete(ctx, resourceGroupName, name)
future, err := ac.disks.Delete(ctx, spec.ResourceGroupName(), spec.ResourceName())
if err != nil {
return err
return nil, err
}

ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout)
defer cancel()

err = future.WaitForCompletionRef(ctx, ac.disks.Client)
if err != nil {
return err
// if an error occurs, return the future.
// this means the long-running operation didn't finish in the specified timeout.
return &future, err
}
_, err = future.Result(ac.disks)
return err
// if the operation completed, return a nil future.
return nil, err
}

func (ac *AzureClient) Result(ctx context.Context, futureData azureautorest.FutureAPI, futureType string) (interface{}, error) {
return nil, nil
}

// IsDone returns true if the long-running operation has completed.
func (ac *AzureClient) IsDone(ctx context.Context, future azureautorest.FutureAPI) (bool, error) {
ctx, span := tele.Tracer().Start(ctx, "disks.AzureClient.IsDone")
defer span.End()

done, err := future.DoneWithContext(ctx, ac.disks)
if err != nil {
return false, errors.Wrap(err, "failed checking if the operation was complete")
}

return done, nil
}
42 changes: 25 additions & 17 deletions azure/services/disks/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,35 @@ import (
"context"

"github.com/go-logr/logr"
"github.com/pkg/errors"

infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
"sigs.k8s.io/cluster-api-provider-azure/azure"
"sigs.k8s.io/cluster-api-provider-azure/azure/services/async"
"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
"sigs.k8s.io/cluster-api-provider-azure/util/tele"
)

const serviceName = "disks"

// DiskScope defines the scope interface for a disk service.
type DiskScope interface {
logr.Logger
azure.ClusterDescriber
DiskSpecs() []azure.DiskSpec
azure.AsyncStatusUpdater
DiskSpecs() []azure.ResourceSpecGetter
}

// Service provides operations on Azure resources.
type Service struct {
Scope DiskScope
client
Client
}

// New creates a new disks service.
func New(scope DiskScope) *Service {
return &Service{
Scope: scope,
client: newClient(scope),
Client: NewClient(scope),
}
}

Expand All @@ -57,21 +62,24 @@ func (s *Service) Reconcile(ctx context.Context) error {

// Delete deletes the disk associated with a VM.
func (s *Service) Delete(ctx context.Context) error {
ctx, _, done := tele.StartSpanWithLogger(ctx, "disks.Service.Delete")
defer done()
ctx, span := tele.Tracer().Start(ctx, "disks.Service.Delete")
defer span.End()

ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureServiceReconcileTimeout)
defer cancel()

var result error

// We go through the list of DiskSpecs to delete each one, independently of the result of the previous one.
// If multiple erros occur, we return the most pressing one
// order of precedence is: error deleting -> deleting in progress -> deleted (no error)
for _, diskSpec := range s.Scope.DiskSpecs() {
s.Scope.V(2).Info("deleting disk", "disk", diskSpec.Name)
err := s.client.Delete(ctx, s.Scope.ResourceGroup(), diskSpec.Name)
if err != nil && azure.ResourceNotFound(err) {
// already deleted
continue
if err := async.DeleteResource(ctx, s.Scope, s.Client, diskSpec, serviceName); err != nil {
if !azure.IsOperationNotDoneError(err) || result == nil {
result = err
}
}
if err != nil {
return errors.Wrapf(err, "failed to delete disk %s in resource group %s", diskSpec.Name, s.Scope.ResourceGroup())
}

s.Scope.V(2).Info("successfully deleted disk", "disk", diskSpec.Name)
}
return nil
s.Scope.UpdateDeleteStatus(infrav1.DisksReadyCondition, serviceName, result)
return result
}
71 changes: 52 additions & 19 deletions azure/services/disks/mock_disks/client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ca63d3f

Please sign in to comment.