Skip to content
Open
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: 1 addition & 1 deletion api/v1/zz_generated.deepcopy.go

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

16 changes: 9 additions & 7 deletions controllers/staticroute/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type reconcileImplClientMock struct {
getErr error
updateErr error
listErr error
listCalls int
}

func (m reconcileImplClientMock) Get(ctx context.Context, key client.ObjectKey, obj client.Object, options ...client.GetOption) error {
Expand All @@ -53,10 +54,11 @@ func (m reconcileImplClientMock) Update(ctx context.Context, obj client.Object,
return m.client.Update(ctx, obj, options...)
}

func (m reconcileImplClientMock) List(ctx context.Context, list client.ObjectList, options ...client.ListOption) error {
if m.listErr != nil {
func (m *reconcileImplClientMock) List(ctx context.Context, list client.ObjectList, options ...client.ListOption) error {
if m.listErr != nil && m.listCalls > 0 {
return m.listErr
}
m.listCalls++
return m.client.List(ctx, list, options...)
}

Expand Down Expand Up @@ -140,15 +142,15 @@ func (m routeManagerMock) Run(chan struct{}) error {
return nil
}

func newFakeClient(route *staticroutev1.StaticRoute) client.Client {
func newFakeClient(route *staticroutev1.StaticRoute, nodes *corev1.NodeList) client.Client {
s := runtime.NewScheme()
s.AddKnownTypes(staticroutev1.GroupVersion, route)
nodes := &corev1.NodeList{}
s.AddKnownTypes(corev1.SchemeGroupVersion, nodes)
s.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.NodeList{})
s.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Node{})
return fake.NewClientBuilder().
WithScheme(s).
WithStatusSubresource(route).
WithRuntimeObjects([]runtime.Object{route}...).
WithRuntimeObjects([]runtime.Object{nodes, route}...).
Build()
}

Expand Down Expand Up @@ -185,7 +187,7 @@ func newStaticRouteWithValues(withSpec, withStatus bool) *staticroutev1.StaticRo
if withStatus {
route.Status = staticroutev1.StaticRouteStatus{
NodeStatus: []staticroutev1.StaticRouteNodeStatus{
staticroutev1.StaticRouteNodeStatus{
{
Hostname: "hostname",
State: staticroutev1.StaticRouteSpec{
Subnet: "10.0.0.1/16",
Expand Down
58 changes: 57 additions & 1 deletion controllers/staticroute/staticroute_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ func reconcileImpl(params reconcileImplParams) (res *reconcile.Result, err error

rw := routeWrapper{instance: instance}

// Cleanup is only needed when the StaticRoute is being deleted.
// Do not list all Nodes during normal reconciliation.
if !instance.DeletionTimestamp.IsZero() {
if res, err = removeStaleNodeStatuses(params, instance, reqLogger); err != nil {
return res, err
}
}

defer func() {
if !reportStatus {
return
Expand Down Expand Up @@ -227,7 +235,7 @@ func reconcileImpl(params reconcileImplParams) (res *reconcile.Result, err error
isChanged ||
selectorNoLongerMatches {
reportStatus = false
if !rw.removeFromStatus(params.options.Hostname) {
if !rw.removeFromStatus(params.options.Hostname) && len(rw.instance.Status.NodeStatus) > 0 {
return alreadyDeleted, nil
}
res, err = deleteOperation(params, &rw, reqLogger)
Expand Down Expand Up @@ -440,3 +448,51 @@ func convertToOperator(operator metav1.LabelSelectorOperator) (selection.Operato
return selection.Equals, errors.New("unable to convert operator")
}
}

// removeStaleNodeStatuses removes status entries for nodes that no longer
// exist. It also releases the finalizer when a deleting StaticRoute has no
// remaining node statuses.
func removeStaleNodeStatuses(
Comment thread
FilatovM marked this conversation as resolved.
Resolved
params reconcileImplParams,
instance *staticroutev1.StaticRoute,
logger types.Logger,
) (*reconcile.Result, error) {
nodes := &corev1.NodeList{}
if err := params.client.List(context.Background(), nodes); err != nil {
logger.Error(err, "Unable to list Kubernetes nodes")
return nodeGetError, err
}

existingNodes := make(map[string]struct{}, len(nodes.Items))
for i := range nodes.Items {
node := &nodes.Items[i]

existingNodes[node.Name] = struct{}{}

if hostname := node.Labels[HostNameLabel]; hostname != "" {
existingNodes[hostname] = struct{}{}
}
}

originalLength := len(instance.Status.NodeStatus)
updatedStatus := make([]staticroutev1.StaticRouteNodeStatus, 0, originalLength)
for _, status := range instance.Status.NodeStatus {
if _, exists := existingNodes[status.Hostname]; exists {
updatedStatus = append(updatedStatus, status)
continue
}
logger.Info("Removing stale node status", "hostname", status.Hostname)
}

if len(updatedStatus) == originalLength {
return alreadyDeleted, nil
}

instance.Status.NodeStatus = updatedStatus
if err := params.client.Status().Update(context.Background(), instance); err != nil {
logger.Error(err, "Unable to update StaticRoute status after stale-node cleanup")
return delStatusUpdateError, err
}

return finished, nil
}
Loading