Skip to content

Commit ff49c7a

Browse files
authored
Merge pull request #1861 from Jont828/async-availabilitysets
Make availability set reconcile/delete async
2 parents f49dc68 + 020a2b6 commit ff49c7a

9 files changed

Lines changed: 613 additions & 263 deletions

File tree

azure/scope/machine.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424
"time"
2525

26+
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute"
2627
"github.com/Azure/go-autorest/autorest/to"
2728
"github.com/pkg/errors"
2829
corev1 "k8s.io/api/core/v1"
@@ -37,6 +38,7 @@ import (
3738

3839
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
3940
"sigs.k8s.io/cluster-api-provider-azure/azure"
41+
"sigs.k8s.io/cluster-api-provider-azure/azure/services/availabilitysets"
4042
"sigs.k8s.io/cluster-api-provider-azure/azure/services/disks"
4143
"sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus"
4244
"sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachines"
@@ -94,9 +96,10 @@ type MachineScope struct {
9496

9597
// MachineCache stores common machine information so we don't have to hit the API multiple times within the same reconcile loop.
9698
type MachineCache struct {
97-
BootstrapData string
98-
VMImage *infrav1.Image
99-
VMSKU resourceskus.SKU
99+
BootstrapData string
100+
VMImage *infrav1.Image
101+
VMSKU resourceskus.SKU
102+
availabilitySetSKU resourceskus.SKU
100103
}
101104

102105
// InitMachineCache sets cached information about the machine to be used in the scope.
@@ -122,9 +125,16 @@ func (m *MachineScope) InitMachineCache(ctx context.Context) error {
122125
if err != nil {
123126
return err
124127
}
128+
125129
m.cache.VMSKU, err = skuCache.Get(ctx, m.AzureMachine.Spec.VMSize, resourceskus.VirtualMachines)
126130
if err != nil {
127-
return azure.WithTerminalError(errors.Wrapf(err, "failed to get SKU %s in compute api", m.AzureMachine.Spec.VMSize))
131+
return azure.WithTerminalError(errors.Wrapf(err, "failed to get VM SKU %s in compute api", m.AzureMachine.Spec.VMSize))
132+
}
133+
134+
m.cache.availabilitySetSKU, err = skuCache.Get(ctx, string(compute.AvailabilitySetSkuTypesAligned), resourceskus.AvailabilitySets)
135+
if err != nil {
136+
// TODO: verify error message
137+
return azure.WithTerminalError(errors.Wrapf(err, "failed to get availability set SKU %s in compute api", string(compute.AvailabilitySetSkuTypesAligned)))
128138
}
129139
}
130140

@@ -363,6 +373,29 @@ func (m *MachineScope) ProviderID() string {
363373
return parsed.String()
364374
}
365375

376+
// AvailabilitySet returns the availability set for this machine if available.
377+
func (m *MachineScope) AvailabilitySetSpec() azure.ResourceSpecGetter {
378+
availabilitySetName, ok := m.AvailabilitySet()
379+
if !ok {
380+
return nil
381+
}
382+
383+
spec := &availabilitysets.AvailabilitySetSpec{
384+
Name: availabilitySetName,
385+
ResourceGroup: m.ResourceGroup(),
386+
ClusterName: m.ClusterName(),
387+
Location: m.Location(),
388+
SKU: nil,
389+
AdditionalTags: m.AdditionalTags(),
390+
}
391+
392+
if m.cache != nil {
393+
spec.SKU = &m.cache.availabilitySetSKU
394+
}
395+
396+
return spec
397+
}
398+
366399
// AvailabilitySet returns the availability set for this machine if available.
367400
func (m *MachineScope) AvailabilitySet() (string, bool) {
368401
if !m.AvailabilitySetEnabled() {

azure/services/availabilitysets/availabilitysets.go

Lines changed: 49 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -18,134 +18,98 @@ package availabilitysets
1818

1919
import (
2020
"context"
21-
"strconv"
2221

2322
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute"
24-
"github.com/Azure/go-autorest/autorest/to"
2523
"github.com/pkg/errors"
2624
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
2725
"sigs.k8s.io/cluster-api-provider-azure/azure"
28-
"sigs.k8s.io/cluster-api-provider-azure/azure/converters"
26+
"sigs.k8s.io/cluster-api-provider-azure/azure/services/async"
2927
"sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus"
28+
"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
3029
"sigs.k8s.io/cluster-api-provider-azure/util/tele"
3130
)
3231

32+
const serviceName = "availabilitysets"
33+
3334
// AvailabilitySetScope defines the scope interface for a availability sets service.
3435
type AvailabilitySetScope interface {
3536
azure.ClusterDescriber
36-
AvailabilitySet() (string, bool)
37+
azure.AsyncStatusUpdater
38+
AvailabilitySetSpec() azure.ResourceSpecGetter
3739
}
3840

3941
// Service provides operations on Azure resources.
4042
type Service struct {
4143
Scope AvailabilitySetScope
4244
Client
45+
async.Reconciler
4346
resourceSKUCache *resourceskus.Cache
4447
}
4548

4649
// New creates a new availability sets service.
4750
func New(scope AvailabilitySetScope, skuCache *resourceskus.Cache) *Service {
51+
client := NewClient(scope)
4852
return &Service{
4953
Scope: scope,
50-
Client: NewClient(scope),
54+
Client: client,
5155
resourceSKUCache: skuCache,
56+
Reconciler: async.New(scope, client, client),
5257
}
5358
}
5459

5560
// Reconcile creates or updates availability sets.
5661
func (s *Service) Reconcile(ctx context.Context) error {
57-
ctx, log, done := tele.StartSpanWithLogger(
58-
ctx,
59-
"availabilitysets.Service.Reconcile",
60-
)
62+
ctx, log, done := tele.StartSpanWithLogger(ctx, "availabilitysets.Service.Reconcile")
6163
defer done()
6264

63-
availabilitySetName, ok := s.Scope.AvailabilitySet()
64-
if !ok {
65-
return nil
66-
}
67-
68-
asSku, err := s.resourceSKUCache.Get(ctx, string(compute.AvailabilitySetSkuTypesAligned), resourceskus.AvailabilitySets)
69-
if err != nil {
70-
return errors.Wrap(err, "failed to get availability sets sku")
71-
}
72-
73-
faultDomainCountStr, ok := asSku.GetCapability(resourceskus.MaximumPlatformFaultDomainCount)
74-
if !ok {
75-
return errors.Errorf("cannot find capability %s sku %s", resourceskus.MaximumPlatformFaultDomainCount, *asSku.Name)
76-
}
65+
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureServiceReconcileTimeout)
66+
defer cancel()
7767

78-
faultDomainCount, err := strconv.ParseUint(faultDomainCountStr, 10, 32)
79-
if err != nil {
80-
return errors.Wrap(err, "failed to determine max fault domain count")
68+
setSpec := s.Scope.AvailabilitySetSpec()
69+
var err error
70+
if setSpec != nil {
71+
_, err = s.CreateResource(ctx, setSpec, serviceName)
72+
} else {
73+
log.V(2).Info("skip creation when no availability set spec is found")
8174
}
8275

83-
log.V(2).Info("creating availability set", "availability set", availabilitySetName)
84-
85-
asParams := compute.AvailabilitySet{
86-
Sku: &compute.Sku{
87-
Name: to.StringPtr(string(compute.AvailabilitySetSkuTypesAligned)),
88-
},
89-
AvailabilitySetProperties: &compute.AvailabilitySetProperties{
90-
PlatformFaultDomainCount: to.Int32Ptr(int32(faultDomainCount)),
91-
},
92-
Tags: converters.TagsToMap(infrav1.Build(infrav1.BuildParams{
93-
ClusterName: s.Scope.ClusterName(),
94-
Lifecycle: infrav1.ResourceLifecycleOwned,
95-
Name: to.StringPtr(availabilitySetName),
96-
Role: to.StringPtr(infrav1.CommonRole),
97-
Additional: s.Scope.AdditionalTags(),
98-
})),
99-
Location: to.StringPtr(s.Scope.Location()),
100-
}
101-
102-
_, err = s.Client.CreateOrUpdate(ctx, s.Scope.ResourceGroup(), availabilitySetName, asParams)
103-
if err != nil {
104-
return errors.Wrapf(err, "failed to create availability set %s", availabilitySetName)
105-
}
106-
107-
log.V(2).Info("successfully created availability set", "availability set", availabilitySetName)
108-
109-
return nil
76+
s.Scope.UpdatePutStatus(infrav1.AvailabilitySetReadyCondition, serviceName, err)
77+
return err
11078
}
11179

11280
// Delete deletes availability sets.
11381
func (s *Service) Delete(ctx context.Context) error {
11482
ctx, log, done := tele.StartSpanWithLogger(ctx, "availabilitysets.Service.Delete")
11583
defer done()
11684

117-
availabilitySetName, ok := s.Scope.AvailabilitySet()
118-
if !ok {
119-
return nil
85+
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureServiceReconcileTimeout)
86+
defer cancel()
87+
88+
setSpec := s.Scope.AvailabilitySetSpec()
89+
var resultingErr error
90+
if setSpec == nil {
91+
log.V(2).Info("skip deletion when no availability set spec is found")
92+
} else {
93+
existingSet, err := s.Client.Get(ctx, setSpec)
94+
if err != nil {
95+
if !azure.ResourceNotFound(err) {
96+
resultingErr = errors.Wrapf(err, "failed to get availability set %s in resource group %s", setSpec.ResourceName(), setSpec.ResourceGroupName())
97+
}
98+
} else {
99+
availabilitySet, ok := existingSet.(compute.AvailabilitySet)
100+
if !ok {
101+
resultingErr = errors.Errorf("%T is not a compute.AvailabilitySet", existingSet)
102+
} else {
103+
// only delete when the availability set does not have any vms
104+
if availabilitySet.AvailabilitySetProperties != nil && availabilitySet.VirtualMachines != nil && len(*availabilitySet.VirtualMachines) > 0 {
105+
log.V(2).Info("skip deleting availability set with VMs", "availability set", setSpec.ResourceName())
106+
} else {
107+
resultingErr = s.DeleteResource(ctx, setSpec, serviceName)
108+
}
109+
}
110+
}
120111
}
121112

122-
as, err := s.Client.Get(ctx, s.Scope.ResourceGroup(), availabilitySetName)
123-
if err != nil && azure.ResourceNotFound(err) {
124-
// already deleted
125-
return nil
126-
}
127-
128-
if err != nil {
129-
return errors.Wrapf(err, "failed to get availability set %s in resource group %s", availabilitySetName, s.Scope.ResourceGroup())
130-
}
131-
132-
// only delete when the availability set does not have any vms
133-
if as.AvailabilitySetProperties != nil && as.VirtualMachines != nil && len(*as.VirtualMachines) > 0 {
134-
return nil
135-
}
136-
137-
log.V(2).Info("deleting availability set", "availability set", availabilitySetName)
138-
err = s.Client.Delete(ctx, s.Scope.ResourceGroup(), availabilitySetName)
139-
if err != nil && azure.ResourceNotFound(err) {
140-
// already deleted
141-
return nil
142-
}
143-
144-
if err != nil {
145-
return errors.Wrapf(err, "failed to delete availability set %s in resource group %s", availabilitySetName, s.Scope.ResourceGroup())
146-
}
147-
148-
log.V(2).Info("successfully delete availability set", "availability set", availabilitySetName)
149-
150-
return nil
113+
s.Scope.UpdateDeleteStatus(infrav1.AvailabilitySetReadyCondition, serviceName, resultingErr)
114+
return resultingErr
151115
}

0 commit comments

Comments
 (0)