Skip to content

Commit 5ac795b

Browse files
Merge pull request #198 from wanyufe/cs-events
add events to cloudstack machine resource
2 parents 734b333 + 7f1be54 commit 5ac795b

5 files changed

+98
-18
lines changed

controllers/cloudstackmachine_controller.go

+34-7
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,26 @@ var (
4848
failuredomainMatcher = regexp.MustCompile(`ds\.meta_data\.failuredomain`)
4949
)
5050

51+
const (
52+
BootstrapDataNotReady = "Bootstrap DataSecretName not yet available"
53+
CSMachineCreationSuccess = "CloudStack instance Created"
54+
CSMachineCreationFailed = "Creating CloudStack machine failed: %s"
55+
MachineInstanceRunning = "Machine instance is Running..."
56+
MachineInErrorMessage = "CloudStackMachine VM in error state. Deleting associated Machine"
57+
MachineNotReadyMessage = "Instance not ready, is %s"
58+
CSMachineStateCheckerCreationFailed = "error encountered when creating CloudStackMachineStateChecker"
59+
CSMachineStateCheckerCreationSuccess = "CloudStackMachineStateChecker created"
60+
CSMachineDeletionMessage = "Deleting CloudStack Machine %s"
61+
CSMachineDeletionInstanceIDNotFoundMessage = "Deleting CloudStack Machine %s instanceID not found"
62+
)
63+
5164
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines,verbs=get;list;watch;create;update;patch;delete
5265
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines/status,verbs=get;update;patch
5366
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=cloudstackmachines/finalizers,verbs=update
5467
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines;machines/status,verbs=get;list;watch
5568
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machinesets,verbs=get;list;watch
5669
// +kubebuilder:rbac:groups=controlplane.cluster.x-k8s.io,resources=kubeadmcontrolplanes,verbs=get;list;watch
70+
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
5771

5872
// CloudStackMachineReconciliationRunner is a ReconciliationRunner with extensions specific to CloudStack machine reconciliation.
5973
type CloudStackMachineReconciliationRunner struct {
@@ -186,7 +200,8 @@ func (r *CloudStackMachineReconciliationRunner) DeleteMachineIfFailuredomainNotE
186200
// Implicitly it also fetches its bootstrap secret in order to create said instance.
187201
func (r *CloudStackMachineReconciliationRunner) GetOrCreateVMInstance() (retRes ctrl.Result, reterr error) {
188202
if r.CAPIMachine.Spec.Bootstrap.DataSecretName == nil {
189-
return r.RequeueWithMessage("Bootstrap DataSecretName not yet available.")
203+
r.Recorder.Event(r.ReconciliationSubject, "Normal", "Creating", BootstrapDataNotReady)
204+
return r.RequeueWithMessage(BootstrapDataNotReady + ".")
190205
}
191206
r.Log.Info("Got Bootstrap DataSecretName.")
192207

@@ -204,8 +219,12 @@ func (r *CloudStackMachineReconciliationRunner) GetOrCreateVMInstance() (retRes
204219
userData := processCustomMetadata(data, r)
205220
err := r.CSUser.GetOrCreateVMInstance(r.ReconciliationSubject, r.CAPIMachine, r.CSCluster, r.FailureDomain, r.AffinityGroup, userData)
206221

222+
if err != nil {
223+
r.Recorder.Eventf(r.ReconciliationSubject, "Warning", "Creating", CSMachineCreationFailed, err.Error())
224+
}
207225
if err == nil && !controllerutil.ContainsFinalizer(r.ReconciliationSubject, infrav1.MachineFinalizer) { // Fetched or Created?
208-
r.Log.Info("CloudStack instance Created", "instanceStatus", r.ReconciliationSubject.Status)
226+
r.Recorder.Eventf(r.ReconciliationSubject, "Normal", "Created", CSMachineCreationSuccess)
227+
r.Log.Info(CSMachineCreationSuccess, "instanceStatus", r.ReconciliationSubject.Status)
209228
}
210229
// Always add the finalizer regardless. It can't be added twice anyway.
211230
controllerutil.AddFinalizer(r.ReconciliationSubject, infrav1.MachineFinalizer)
@@ -223,16 +242,19 @@ func processCustomMetadata(data []byte, r *CloudStackMachineReconciliationRunner
223242
// ConfirmVMStatus checks the Instance's status for running state and requeues otherwise.
224243
func (r *CloudStackMachineReconciliationRunner) RequeueIfInstanceNotRunning() (retRes ctrl.Result, reterr error) {
225244
if r.ReconciliationSubject.Status.InstanceState == "Running" {
226-
r.Log.Info("Machine instance is Running...")
245+
r.Recorder.Event(r.ReconciliationSubject, "Normal", "Running", MachineInstanceRunning)
246+
r.Log.Info(MachineInstanceRunning)
227247
r.ReconciliationSubject.Status.Ready = true
228248
} else if r.ReconciliationSubject.Status.InstanceState == "Error" {
229-
r.Log.Info("CloudStackMachine VM in error state. Deleting associated Machine.", "csMachine", r.ReconciliationSubject.GetName())
249+
r.Recorder.Event(r.ReconciliationSubject, "Warning", "Error", MachineInErrorMessage)
250+
r.Log.Info(MachineInErrorMessage, "csMachine", r.ReconciliationSubject.GetName())
230251
if err := r.K8sClient.Delete(r.RequestCtx, r.CAPIMachine); err != nil {
231252
return ctrl.Result{}, err
232253
}
233254
return ctrl.Result{RequeueAfter: utils.RequeueTimeout}, nil
234255
} else {
235-
r.Log.Info(fmt.Sprintf("Instance not ready, is %s.", r.ReconciliationSubject.Status.InstanceState))
256+
r.Recorder.Eventf(r.ReconciliationSubject, "Warning", r.ReconciliationSubject.Status.InstanceState, MachineNotReadyMessage, r.ReconciliationSubject.Status.InstanceState)
257+
r.Log.Info(fmt.Sprintf(MachineNotReadyMessage, r.ReconciliationSubject.Status.InstanceState))
236258
return ctrl.Result{RequeueAfter: utils.RequeueTimeout}, nil
237259
}
238260
return ctrl.Result{}, nil
@@ -263,9 +285,10 @@ func (r *CloudStackMachineReconciliationRunner) GetOrCreateMachineStateChecker()
263285
}
264286

265287
if err := r.K8sClient.Create(r.RequestCtx, csMachineStateChecker); err != nil && !utils.ContainsAlreadyExistsSubstring(err) {
266-
return r.ReturnWrappedError(err, "error encountered when creating CloudStackMachineStateChecker")
288+
r.Recorder.Eventf(r.ReconciliationSubject, "Warning", "Machine State Checker", CSMachineStateCheckerCreationFailed)
289+
return r.ReturnWrappedError(err, CSMachineStateCheckerCreationFailed)
267290
}
268-
291+
r.Recorder.Eventf(r.ReconciliationSubject, "Normal", "Machine State Checker", CSMachineStateCheckerCreationSuccess)
269292
return r.GetObjectByName(*checkerName, r.StateChecker)()
270293
}
271294

@@ -280,9 +303,12 @@ func (r *CloudStackMachineReconciliationRunner) ReconcileDelete() (retRes ctrl.R
280303
fmt.Sprintf(" If this VM has already been deleted, please remove the finalizer named %s from object %s",
281304
"cloudstackmachine.infrastructure.cluster.x-k8s.io", r.ReconciliationSubject.Name))
282305
// Cloudstack VM may be not found or more than one found by name
306+
r.Recorder.Eventf(r.ReconciliationSubject, "Warning", "Deleting", CSMachineDeletionInstanceIDNotFoundMessage, r.ReconciliationSubject.Name)
307+
r.Log.Error(err, fmt.Sprintf(CSMachineDeletionInstanceIDNotFoundMessage, r.ReconciliationSubject.Name))
283308
return ctrl.Result{}, err
284309
}
285310
}
311+
r.Recorder.Eventf(r.ReconciliationSubject, "Normal", "Deleting", CSMachineDeletionMessage, r.ReconciliationSubject.Name)
286312
r.Log.Info("Deleting instance", "instance-id", r.ReconciliationSubject.Spec.InstanceID)
287313
// Use CSClient instead of CSUser here to expunge as admin.
288314
// The CloudStack-Go API does not return an error, but the VM won't delete with Expunge set if requested by
@@ -364,6 +390,7 @@ func (reconciler *CloudStackMachineReconciler) SetupWithManager(mgr ctrl.Manager
364390
return err
365391
}
366392

393+
reconciler.Recorder = mgr.GetEventRecorderFor("capc-machine-controller")
367394
// Add a watch on CAPI Cluster objects for unpause and ready events.
368395
return controller.Watch(
369396
&source.Kind{Type: &clusterv1.Cluster{}},

controllers/cloudstackmachine_controller_test.go

+45-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
ctrl "sigs.k8s.io/controller-runtime"
3333
"sigs.k8s.io/controller-runtime/pkg/client"
3434
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
35+
"strings"
3536
)
3637

3738
var _ = Describe("CloudStackMachineReconciler", func() {
@@ -50,7 +51,7 @@ var _ = Describe("CloudStackMachineReconciler", func() {
5051

5152
// Setup a failure domain for the machine reconciler to find.
5253
Ω(k8sClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed())
53-
setClusterReady()
54+
setClusterReady(k8sClient)
5455
})
5556

5657
It("Should call GetOrCreateVMInstance and set Status.Ready to true", func() {
@@ -197,8 +198,9 @@ var _ = Describe("CloudStackMachineReconciler", func() {
197198

198199
Context("With a fake ctrlRuntimeClient and no test Env at all.", func() {
199200
BeforeEach(func() {
200-
dummies.SetDummyVars()
201201
setupFakeTestClient()
202+
dummies.CSCluster.Spec.FailureDomains = dummies.CSCluster.Spec.FailureDomains[:1]
203+
dummies.CSCluster.Spec.FailureDomains[0].Name = dummies.CSFailureDomain1.Spec.Name
202204
})
203205

204206
It("Should exit having not found a failure domain to place the machine in.", func() {
@@ -219,5 +221,46 @@ var _ = Describe("CloudStackMachineReconciler", func() {
219221
Ω(err).ShouldNot(HaveOccurred())
220222
Ω(res.RequeueAfter).ShouldNot(BeZero())
221223
})
224+
225+
It("Should create event Machine instance is Running", func() {
226+
key := client.ObjectKeyFromObject(dummies.CSCluster)
227+
dummies.CAPIMachine.Name = "someMachine"
228+
dummies.CAPIMachine.Spec.Bootstrap.DataSecretName = &dummies.BootstrapSecret.Name
229+
dummies.CSMachine1.OwnerReferences = append(dummies.CSMachine1.OwnerReferences, metav1.OwnerReference{
230+
Kind: "Machine",
231+
APIVersion: clusterv1.GroupVersion.String(),
232+
Name: dummies.CAPIMachine.Name,
233+
UID: "uniqueness",
234+
})
235+
mockCloudClient.EXPECT().GetOrCreateVMInstance(
236+
gomock.Any(), gomock.Any(), gomock.Any(),
237+
gomock.Any(), gomock.Any(), gomock.Any()).Do(
238+
func(arg1, _, _, _, _, _ interface{}) {
239+
arg1.(*infrav1.CloudStackMachine).Status.InstanceState = "Running"
240+
}).AnyTimes()
241+
Ω(fakeCtrlClient.Get(ctx, key, dummies.CSCluster)).Should(Succeed())
242+
Ω(fakeCtrlClient.Create(ctx, dummies.CAPIMachine)).Should(Succeed())
243+
Ω(fakeCtrlClient.Create(ctx, dummies.CSMachine1)).Should(Succeed())
244+
Ω(fakeCtrlClient.Create(ctx, dummies.CSFailureDomain1)).Should(Succeed())
245+
Ω(fakeCtrlClient.Create(ctx, dummies.ACSEndpointSecret1)).Should(Succeed())
246+
Ω(fakeCtrlClient.Create(ctx, dummies.BootstrapSecret)).Should(Succeed())
247+
248+
setClusterReady(fakeCtrlClient)
249+
250+
requestNamespacedName := types.NamespacedName{Namespace: dummies.ClusterNameSpace, Name: dummies.CSMachine1.Name}
251+
MachineReconciler.AsFailureDomainUser(&dummies.CSFailureDomain1.Spec)
252+
res, err := MachineReconciler.Reconcile(ctx, ctrl.Request{NamespacedName: requestNamespacedName})
253+
Ω(err).ShouldNot(HaveOccurred())
254+
Ω(res.RequeueAfter).Should(BeZero())
255+
256+
Eventually(func() bool {
257+
for event := range fakeRecorder.Events {
258+
return strings.Contains(event, "Normal Created CloudStack instance Created") ||
259+
strings.Contains(event, "Normal Running Machine instance is Running...") ||
260+
strings.Contains(event, "Normal Machine State Checker CloudStackMachineStateChecker created")
261+
}
262+
return false
263+
}, timeout).Should(BeTrue())
264+
})
222265
})
223266
})

controllers/controllers_suite_test.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"flag"
2222
"fmt"
2323
"go/build"
24+
"k8s.io/client-go/tools/record"
2425
"os"
2526
"os/exec"
2627
"path/filepath"
@@ -69,8 +70,9 @@ var (
6970
)
7071

7172
const (
72-
timeout = 10 * time.Second
73-
pollInterval = 1 * time.Second
73+
timeout = 10 * time.Second
74+
pollInterval = 1 * time.Second
75+
fakeEventBufferSize = 10
7476
)
7577

7678
func envOr(envKey, defaultValue string) string {
@@ -114,6 +116,7 @@ var (
114116
cfg *rest.Config
115117
logger logr.Logger
116118
fakeCtrlClient client.Client
119+
fakeRecorder *record.FakeRecorder
117120

118121
// Mock Vars.
119122
mockCtrl *gomock.Controller
@@ -254,17 +257,20 @@ func setupFakeTestClient() {
254257

255258
// Make a fake k8s client with CloudStack and CAPI cluster.
256259
fakeCtrlClient = fake.NewClientBuilder().WithObjects(dummies.CSCluster, dummies.CAPICluster).Build()
257-
260+
fakeRecorder = record.NewFakeRecorder(fakeEventBufferSize)
258261
// Setup mock clients.
259262
mockCSAPIClient = cloudstack.NewMockClient(mockCtrl)
260263
mockCloudClient = mocks.NewMockClient(mockCtrl)
261264

262265
// Base reconciler shared across reconcilers.
263266
base := csCtrlrUtils.ReconcilerBase{
264-
K8sClient: fakeCtrlClient,
265-
Scheme: scheme.Scheme,
266-
CSClient: mockCloudClient,
267-
BaseLogger: logger}
267+
K8sClient: fakeCtrlClient,
268+
Scheme: scheme.Scheme,
269+
CSClient: mockCloudClient,
270+
BaseLogger: logger,
271+
Recorder: fakeRecorder,
272+
CloudClientExtension: &MockCtrlrCloudClientImplementation{},
273+
}
268274

269275
ctx, cancel = context.WithCancel(context.TODO())
270276

@@ -312,9 +318,9 @@ var _ = AfterEach(func() {
312318
var _ = AfterSuite(func() {})
313319

314320
// setClusterReady patches the clsuter with ready status true.
315-
func setClusterReady() {
321+
func setClusterReady(client client.Client) {
316322
Eventually(func() error {
317-
ph, err := patch.NewHelper(dummies.CSCluster, k8sClient)
323+
ph, err := patch.NewHelper(dummies.CSCluster, client)
318324
Ω(err).ShouldNot(HaveOccurred())
319325
dummies.CSCluster.Status.Ready = true
320326
return ph.Patch(ctx, dummies.CSCluster, patch.WithStatusObservedGeneration{})

controllers/utils/base_reconciler.go

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package utils
1919
import (
2020
"context"
2121
"fmt"
22+
"k8s.io/client-go/tools/record"
2223
"strings"
2324
"time"
2425

@@ -46,6 +47,7 @@ type ReconcilerBase struct {
4647
Scheme *runtime.Scheme
4748
K8sClient client.Client
4849
CSClient cloud.Client
50+
Recorder record.EventRecorder
4951
CloudClientExtension
5052
}
5153

@@ -453,6 +455,7 @@ func (r *ReconcilerBase) InitFromMgr(mgr ctrl.Manager, client cloud.Client) {
453455
r.K8sClient = mgr.GetClient()
454456
r.BaseLogger = ctrl.Log.WithName("controllers")
455457
r.Scheme = mgr.GetScheme()
458+
r.Recorder = mgr.GetEventRecorderFor("capc-controller-manager")
456459
r.CSClient = client
457460
}
458461

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ func main() {
148148
base := utils.ReconcilerBase{
149149
K8sClient: mgr.GetClient(),
150150
BaseLogger: ctrl.Log.WithName("controllers"),
151+
Recorder: mgr.GetEventRecorderFor("capc-controller-manager"),
151152
Scheme: mgr.GetScheme()}
152153

153154
setupReconcilers(base, mgr)

0 commit comments

Comments
 (0)