Skip to content

Commit 9765463

Browse files
committed
feat: bound all reconcile loops and network comms with context
1 parent 1274b05 commit 9765463

18 files changed

+323
-163
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
!/feature/**
1010
!/pkg/**
1111
!/version/**
12+
!/util/**
1213
!/main.go
1314
!/go.mod
1415
!/go.sum

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
run:
2-
deadline: 3m
2+
deadline: 5m
33
skip-dirs:
44
- mock*
55
skip-files:

cloud/scope/cluster.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
6868
Cluster: params.Cluster,
6969
AzureCluster: params.AzureCluster,
7070
patchHelper: helper,
71-
Context: context.Background(),
7271
}, nil
7372
}
7473

@@ -81,7 +80,6 @@ type ClusterScope struct {
8180
AzureClients
8281
Cluster *clusterv1.Cluster
8382
AzureCluster *infrav1.AzureCluster
84-
Context context.Context
8583
}
8684

8785
// Network returns the cluster network object.
@@ -142,13 +140,13 @@ func (s *ClusterScope) ListOptionsLabelSelector() client.ListOption {
142140
}
143141

144142
// PatchObject persists the cluster configuration and status.
145-
func (s *ClusterScope) PatchObject() error {
146-
return s.patchHelper.Patch(context.TODO(), s.AzureCluster)
143+
func (s *ClusterScope) PatchObject(ctx context.Context) error {
144+
return s.patchHelper.Patch(ctx, s.AzureCluster)
147145
}
148146

149147
// Close closes the current scope persisting the cluster configuration and status.
150-
func (s *ClusterScope) Close() error {
151-
return s.patchHelper.Patch(context.TODO(), s.AzureCluster)
148+
func (s *ClusterScope) Close(ctx context.Context) error {
149+
return s.patchHelper.Patch(ctx, s.AzureCluster)
152150
}
153151

154152
// AdditionalTags returns AdditionalTags from the scope's AzureCluster.

cloud/scope/machine.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,13 @@ func (m *MachineScope) SetAddresses(addrs []corev1.NodeAddress) {
210210
}
211211

212212
// PatchObject persists the machine spec and status.
213-
func (m *MachineScope) PatchObject() error {
214-
return m.patchHelper.Patch(context.TODO(), m.AzureMachine)
213+
func (m *MachineScope) PatchObject(ctx context.Context) error {
214+
return m.patchHelper.Patch(ctx, m.AzureMachine)
215215
}
216216

217217
// Close the MachineScope by updating the machine spec, machine status.
218-
func (m *MachineScope) Close() error {
219-
return m.patchHelper.Patch(context.TODO(), m.AzureMachine)
218+
func (m *MachineScope) Close(ctx context.Context) error {
219+
return m.patchHelper.Patch(ctx, m.AzureMachine)
220220
}
221221

222222
// AdditionalTags merges AdditionalTags from the scope's AzureCluster and AzureMachine. If the same key is present in both,
@@ -233,13 +233,13 @@ func (m *MachineScope) AdditionalTags() infrav1.Tags {
233233
}
234234

235235
// GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName.
236-
func (m *MachineScope) GetBootstrapData() (string, error) {
236+
func (m *MachineScope) GetBootstrapData(ctx context.Context) (string, error) {
237237
if m.Machine.Spec.Bootstrap.DataSecretName == nil {
238238
return "", errors.New("error retrieving bootstrap data: linked Machine's bootstrap.dataSecretName is nil")
239239
}
240240
secret := &corev1.Secret{}
241241
key := types.NamespacedName{Namespace: m.Namespace(), Name: *m.Machine.Spec.Bootstrap.DataSecretName}
242-
if err := m.client.Get(context.TODO(), key, secret); err != nil {
242+
if err := m.client.Get(ctx, key, secret); err != nil {
243243
return "", errors.Wrapf(err, "failed to retrieve bootstrap data secret for AzureMachine %s/%s", m.Namespace(), m.Name())
244244
}
245245

cloud/scope/machinepool.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,8 @@ func (m *MachinePoolScope) SetAnnotation(key, value string) {
152152
}
153153

154154
// PatchObject persists the machine spec and status.
155-
func (m *MachinePoolScope) PatchObject() error {
156-
// TODO[dj]: any function we are building where we are not adding context to the signature is welcoming unbound execution times. This needs to be addressed.
157-
return m.patchHelper.Patch(context.TODO(), m.AzureMachinePool)
155+
func (m *MachinePoolScope) PatchObject(ctx context.Context) error {
156+
return m.patchHelper.Patch(ctx, m.AzureMachinePool)
158157
}
159158

160159
func (m *MachinePoolScope) AzureMachineTemplate(ctx context.Context) (*infrav1.AzureMachineTemplate, error) {
@@ -163,8 +162,8 @@ func (m *MachinePoolScope) AzureMachineTemplate(ctx context.Context) (*infrav1.A
163162
}
164163

165164
// Close the MachineScope by updating the machine spec, machine status.
166-
func (m *MachinePoolScope) Close() error {
167-
return m.patchHelper.Patch(context.TODO(), m.AzureMachinePool)
165+
func (m *MachinePoolScope) Close(ctx context.Context) error {
166+
return m.patchHelper.Patch(ctx, m.AzureMachinePool)
168167
}
169168

170169
func getAzureMachineTemplate(ctx context.Context, c client.Client, name, namespace string) (*infrav1.AzureMachineTemplate, error) {
@@ -177,14 +176,14 @@ func getAzureMachineTemplate(ctx context.Context, c client.Client, name, namespa
177176
}
178177

179178
// GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName.
180-
func (m *MachinePoolScope) GetBootstrapData() (string, error) {
179+
func (m *MachinePoolScope) GetBootstrapData(ctx context.Context) (string, error) {
181180
dataSecretName := m.MachinePool.Spec.Template.Spec.Bootstrap.DataSecretName
182181
if dataSecretName == nil {
183182
return "", errors.New("error retrieving bootstrap data: linked Machine Spec's bootstrap.dataSecretName is nil")
184183
}
185184
secret := &corev1.Secret{}
186185
key := types.NamespacedName{Namespace: m.AzureMachinePool.Namespace, Name: *dataSecretName}
187-
if err := m.client.Get(context.TODO(), key, secret); err != nil {
186+
if err := m.client.Get(ctx, key, secret); err != nil {
188187
return "", errors.Wrapf(err, "failed to retrieve bootstrap data secret for AzureMachinePool %s/%s", m.AzureMachinePool.Namespace, m.Name())
189188
}
190189

controllers/azurecluster_controller.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"k8s.io/client-go/tools/record"
2727
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3"
2828
"sigs.k8s.io/cluster-api-provider-azure/cloud/scope"
29+
"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
30+
2931
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3032
"sigs.k8s.io/cluster-api/util"
3133
ctrl "sigs.k8s.io/controller-runtime"
@@ -38,8 +40,9 @@ import (
3840
// AzureClusterReconciler reconciles a AzureCluster object
3941
type AzureClusterReconciler struct {
4042
client.Client
41-
Log logr.Logger
42-
Recorder record.EventRecorder
43+
Log logr.Logger
44+
Recorder record.EventRecorder
45+
ReconcileTimeout time.Duration
4346
}
4447

4548
func (r *AzureClusterReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
@@ -55,7 +58,8 @@ func (r *AzureClusterReconciler) SetupWithManager(mgr ctrl.Manager, options cont
5558
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=azuremachinetemplates;azuremachinetemplates/status,verbs=get;list;watch
5659

5760
func (r *AzureClusterReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr error) {
58-
ctx := context.TODO()
61+
ctx, cancel := context.WithTimeout(context.Background(), reconciler.DefaultedLoopTimeout(r.ReconcileTimeout))
62+
defer cancel()
5963
log := r.Log.WithValues("namespace", req.Namespace, "AzureCluster", req.Name)
6064

6165
// Fetch the AzureCluster instance
@@ -93,33 +97,32 @@ func (r *AzureClusterReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, ret
9397

9498
// Always close the scope when exiting this function so we can persist any AzureMachine changes.
9599
defer func() {
96-
if err := clusterScope.Close(); err != nil && reterr == nil {
100+
if err := clusterScope.Close(ctx); err != nil && reterr == nil {
97101
reterr = err
98102
}
99103
}()
100104

101105
// Handle deleted clusters
102106
if !azureCluster.DeletionTimestamp.IsZero() {
103-
return r.reconcileDelete(clusterScope)
107+
return r.reconcileDelete(ctx, clusterScope)
104108
}
105109

106110
// Handle non-deleted clusters
107-
return r.reconcileNormal(clusterScope)
111+
return r.reconcileNormal(ctx, clusterScope)
108112
}
109113

110-
func (r *AzureClusterReconciler) reconcileNormal(clusterScope *scope.ClusterScope) (reconcile.Result, error) {
114+
func (r *AzureClusterReconciler) reconcileNormal(ctx context.Context, clusterScope *scope.ClusterScope) (reconcile.Result, error) {
111115
clusterScope.Info("Reconciling AzureCluster")
112-
113116
azureCluster := clusterScope.AzureCluster
114117

115118
// If the AzureCluster doesn't have our finalizer, add it.
116119
controllerutil.AddFinalizer(azureCluster, infrav1.ClusterFinalizer)
117120
// Register the finalizer immediately to avoid orphaning Azure resources on delete
118-
if err := clusterScope.PatchObject(); err != nil {
121+
if err := clusterScope.PatchObject(ctx); err != nil {
119122
return reconcile.Result{}, err
120123
}
121124

122-
err := newAzureClusterReconciler(clusterScope).Reconcile()
125+
err := newAzureClusterReconciler(clusterScope).Reconcile(ctx)
123126
if err != nil {
124127
return reconcile.Result{}, errors.Wrap(err, "failed to reconcile cluster services")
125128
}
@@ -141,12 +144,12 @@ func (r *AzureClusterReconciler) reconcileNormal(clusterScope *scope.ClusterScop
141144
return reconcile.Result{}, nil
142145
}
143146

144-
func (r *AzureClusterReconciler) reconcileDelete(clusterScope *scope.ClusterScope) (reconcile.Result, error) {
147+
func (r *AzureClusterReconciler) reconcileDelete(ctx context.Context, clusterScope *scope.ClusterScope) (reconcile.Result, error) {
145148
clusterScope.Info("Reconciling AzureCluster delete")
146149

147150
azureCluster := clusterScope.AzureCluster
148151

149-
if err := newAzureClusterReconciler(clusterScope).Delete(); err != nil {
152+
if err := newAzureClusterReconciler(clusterScope).Delete(ctx); err != nil {
150153
return reconcile.Result{}, errors.Wrapf(err, "error deleting AzureCluster %s/%s", azureCluster.Namespace, azureCluster.Name)
151154
}
152155

controllers/azurecluster_controller_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ limitations under the License.
1717
package controllers
1818

1919
import (
20+
"time"
21+
22+
"github.com/go-logr/logr"
23+
"github.com/golang/mock/gomock"
2024
. "github.com/onsi/ginkgo"
2125
. "github.com/onsi/gomega"
2226

2327
"golang.org/x/net/context"
2428
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
2530
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3"
31+
"sigs.k8s.io/cluster-api-provider-azure/internal/test/mock_log"
32+
2633
ctrl "sigs.k8s.io/controller-runtime"
2734
"sigs.k8s.io/controller-runtime/pkg/client"
2835
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -56,5 +63,29 @@ var _ = Describe("AzureClusterReconciler", func() {
5663
Expect(err).To(BeNil())
5764
Expect(result.RequeueAfter).To(BeZero())
5865
})
66+
67+
It("should fail with context timeout error if context expires", func() {
68+
log := mock_log.NewMockLogger(gomock.NewController(GinkgoT()))
69+
log.EXPECT().WithValues(gomock.Any()).DoAndReturn(func(args ...interface{}) logr.Logger {
70+
time.Sleep(1 * time.Second)
71+
return log
72+
})
73+
74+
reconciler := &AzureClusterReconciler{
75+
Client: k8sClient,
76+
Log: log,
77+
ReconcileTimeout: 1 * time.Second,
78+
}
79+
80+
instance := &infrav1.AzureCluster{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}}
81+
_, err := reconciler.Reconcile(ctrl.Request{
82+
NamespacedName: client.ObjectKey{
83+
Namespace: instance.Namespace,
84+
Name: instance.Name,
85+
},
86+
})
87+
Expect(err).To(HaveOccurred())
88+
Expect(err.Error()).To(Or(Equal("context deadline exceeded"), Equal("rate: Wait(n=1) would exceed context deadline")))
89+
})
5990
})
6091
})

0 commit comments

Comments
 (0)