Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build/yaml/crd/legacy/nsx.vmware.com_nsxserviceaccounts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ spec:
type: object
reason:
type: string
supervisorClusterName:
type: string
secrets:
items:
properties:
Expand Down
17 changes: 9 additions & 8 deletions pkg/apis/legacy/v1alpha1/nsxserviceaccount_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,15 @@ type NSXServiceAccountStatus struct {
Reason string `json:"reason,omitempty"`
// Represents the realization status of a NSXServiceAccount's current state.
// Known .status.conditions.type is: "Realized"
Conditions []metav1.Condition `json:"conditions,omitempty"`
VPCPath string `json:"vpcPath,omitempty"`
NSXManagers []string `json:"nsxManagers,omitempty"`
ProxyEndpoints NSXProxyEndpoint `json:"proxyEndpoints,omitempty"`
ClusterID string `json:"clusterID,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
Secrets []NSXSecret `json:"secrets,omitempty"`
NSXRestoreStatus *NSXRestoreStatus `json:"nsxRestoreStatus,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
VPCPath string `json:"vpcPath,omitempty"`
NSXManagers []string `json:"nsxManagers,omitempty"`
ProxyEndpoints NSXProxyEndpoint `json:"proxyEndpoints,omitempty"`
ClusterID string `json:"clusterID,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
SupervisorClusterName string `json:"supervisorClusterName,omitempty"`
Secrets []NSXSecret `json:"secrets,omitempty"`
NSXRestoreStatus *NSXRestoreStatus `json:"nsxRestoreStatus,omitempty"`
}

// +genclient
Expand Down
2 changes: 2 additions & 0 deletions pkg/controllers/inventory/inventory_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var (
watchIngress,
watchNode,
watchNetworkPolicy,
watchNSXServiceAccount,
watchVirtualMachine,
}
)

Expand Down
16 changes: 13 additions & 3 deletions pkg/controllers/inventory/inventory_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,27 @@ func (m *MockCache) List(ctx context.Context, list client.ObjectList, opts ...cl
}
func (m *MockCache) GetInformer(ctx context.Context, obj client.Object, opts ...cache.InformerGetOption) (cache.Informer, error) {
args := m.Called(ctx, obj)
return &MockInformer{}, args.Error(1)
informer, _ := args.Get(0).(cache.Informer)
return informer, args.Error(1)
}

type MockInformer struct {
mock.Mock
handlers toolscache.ResourceEventHandlerFuncs
handlers toolscache.ResourceEventHandlerFuncs
registeredHandler toolscache.ResourceEventHandler
addHandlerErr error
cache.Informer
}

func (m *MockInformer) AddEventHandler(handler toolscache.ResourceEventHandler) (toolscache.ResourceEventHandlerRegistration, error) {
if m != nil && m.handlers.AddFunc != nil {
if m == nil {
return nil, nil
}
if m.addHandlerErr != nil {
return nil, m.addHandlerErr
}
m.registeredHandler = handler
if m.handlers.AddFunc != nil {
m.handlers.AddFunc(handler)
}
return m, nil
Expand Down
126 changes: 126 additions & 0 deletions pkg/controllers/inventory/nsxserviceaccount_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package inventory

import (
"context"
"fmt"
"strings"

vmv1alpha1 "github.com/vmware-tanzu/vm-operator/api/v1alpha1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

nsxvmwarecomv1alpha1 "github.com/vmware-tanzu/nsx-operator/pkg/apis/legacy/v1alpha1"
"github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/inventory"
)

func watchNSXServiceAccount(c *InventoryController, mgr ctrl.Manager) error {
nsxSAInformer, err := mgr.GetCache().GetInformer(context.Background(), &nsxvmwarecomv1alpha1.NSXServiceAccount{})
if err != nil {
log.Error(err, "Failed to create NSXServiceAccount informer")
return err
}

_, err = nsxSAInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
c.handleNSXServiceAccount(obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
c.handleNSXServiceAccount(newObj)
},
DeleteFunc: func(obj interface{}) {
c.handleNSXServiceAccountDelete(obj)
},
})
if err != nil {
log.Error(err, "Failed to add NSXServiceAccount event handler")
return err
}
return nil
}

func (c *InventoryController) handleNSXServiceAccount(obj interface{}) {
var nsxSA *nsxvmwarecomv1alpha1.NSXServiceAccount
switch v := obj.(type) {
case *nsxvmwarecomv1alpha1.NSXServiceAccount:
nsxSA = v
case cache.DeletedFinalStateUnknown:
var ok bool
nsxSA, ok = v.Obj.(*nsxvmwarecomv1alpha1.NSXServiceAccount)
if !ok {
err := fmt.Errorf("obj is not valid *NSXServiceAccount")
log.Error(err, "DeletedFinalStateUnknown Obj is not *NSXServiceAccount")
return
}
}

if nsxSA.Status.Phase != nsxvmwarecomv1alpha1.NSXServiceAccountPhaseRealized {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the exception of the above realization check, handleNSXServiceAccount and handleNSXServiceAccountDelete are exactly the same.

I think it should be ok to have this check even in the deletion handler, so maybe could we just remove handleNSXServiceAccountDelete?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the NSXServiceAccount somehow become unrealized? I separated them because if the NSXServiceAccount becomes unrealized, the update and delete functions will skip it, and the VM's tag won't be removed.

log.Debug("Skip NSXServiceAccount not yet realized", "namespace", nsxSA.Namespace, "name", nsxSA.Name)
return
}

c.enqueueVMsForCluster(nsxSA)
}

func (c *InventoryController) handleNSXServiceAccountDelete(obj interface{}) {
var nsxSA *nsxvmwarecomv1alpha1.NSXServiceAccount
switch v := obj.(type) {
case *nsxvmwarecomv1alpha1.NSXServiceAccount:
nsxSA = v
case cache.DeletedFinalStateUnknown:
var ok bool
nsxSA, ok = v.Obj.(*nsxvmwarecomv1alpha1.NSXServiceAccount)
if !ok {
err := fmt.Errorf("obj is not valid *NSXServiceAccount")
log.Error(err, "DeletedFinalStateUnknown Obj is not *NSXServiceAccount")
return
}
}

log.Info("NSXServiceAccount deleted, enqueuing VMs for tag removal", "namespace", nsxSA.Namespace, "name", nsxSA.Name)
c.enqueueVMsForCluster(nsxSA)
}

// enqueueVMsForCluster lists VirtualMachines belonging to the NSXServiceAccount's
// CAPI cluster and enqueues them to the inventory queue for VM tag processing.
func (c *InventoryController) enqueueVMsForCluster(nsxSA *nsxvmwarecomv1alpha1.NSXServiceAccount) {
clusterName := getClusterNameFromSA(nsxSA)
if clusterName == "" {
log.Info("NSXServiceAccount has no Cluster OwnerReference, skipping VM enqueue",
"namespace", nsxSA.Namespace, "name", nsxSA.Name)
return
}

vmList := &vmv1alpha1.VirtualMachineList{}
if err := c.Client.List(context.Background(), vmList, &client.ListOptions{
Namespace: nsxSA.Namespace,
LabelSelector: labels.SelectorFromSet(labels.Set{inventory.CAPIClusterNameLabel: clusterName}),
}); err != nil {
log.Error(err, "Failed to list VirtualMachines for cluster",
"namespace", nsxSA.Namespace, "cluster", clusterName)
return
}

for i := range vmList.Items {
vm := &vmList.Items[i]
log.Debug("Enqueuing VM from NSXServiceAccount event",
"namespace", vm.Namespace, "name", vm.Name, "cluster", clusterName)
key, _ := keyFunc(vm)
c.inventoryObjectQueue.Add(inventory.InventoryKey{
InventoryType: inventory.InventoryVirtualMachine,
ExternalId: vm.Status.InstanceUUID,
Key: key,
})
}
}

// getClusterNameFromSA extracts the CAPI Cluster name from the NSXServiceAccount's OwnerReferences.
func getClusterNameFromSA(nsxSA *nsxvmwarecomv1alpha1.NSXServiceAccount) string {
for _, ref := range nsxSA.OwnerReferences {
if ref.Kind == "Cluster" && strings.Contains(ref.APIVersion, "cluster.x-k8s.io") {
return ref.Name
}
}
return ""
}
Loading
Loading