diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes.go index a03083f8aca..d15d2659bba 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes.go @@ -111,6 +111,7 @@ func (d *delayedRouteUpdater) updateRoutes() { // No need to do any updating. if len(d.routesToUpdate) == 0 { + klog.V(6).Info("updateRoutes: nothing to update, returning") return } @@ -217,6 +218,8 @@ func (d *delayedRouteUpdater) cleanupOutdatedRoutes(existingRoutes []network.Rou existingRouteName := to.String(existingRoutes[i].Name) split := strings.Split(existingRouteName, routeNameSeparator) + klog.V(4).Infof("cleanupOutdatedRoutes: checking route %s", existingRouteName) + // filter out unmanaged routes deleteRoute := false if d.az.nodeNames.Has(split[0]) { @@ -467,9 +470,8 @@ func (az *Cloud) DeleteRoute(ctx context.Context, clusterName string, kubeRoute return nil } - klog.V(2).Infof("DeleteRoute: deleting route. clusterName=%q instance=%q cidr=%q", clusterName, kubeRoute.TargetNode, kubeRoute.DestinationCIDR) - routeName := mapNodeNameToRouteName(az.ipv6DualStackEnabled, kubeRoute.TargetNode, string(kubeRoute.DestinationCIDR)) + klog.V(2).Infof("DeleteRoute: deleting route. clusterName=%q instance=%q cidr=%q routeName=%q", clusterName, kubeRoute.TargetNode, kubeRoute.DestinationCIDR, routeName) route := network.Route{ Name: to.StringPtr(routeName), RoutePropertiesFormat: &network.RoutePropertiesFormat{}, @@ -487,6 +489,28 @@ func (az *Cloud) DeleteRoute(ctx context.Context, clusterName string, kubeRoute return err } + // Remove outdated ipv4 routes as well + if az.ipv6DualStackEnabled { + routeNameWithoutIPV6Suffix := strings.Split(routeName, routeNameSeparator)[0] + klog.V(2).Infof("DeleteRoute: deleting route. clusterName=%q instance=%q cidr=%q routeName=%q", clusterName, kubeRoute.TargetNode, kubeRoute.DestinationCIDR, routeNameWithoutIPV6Suffix) + route := network.Route{ + Name: to.StringPtr(routeNameWithoutIPV6Suffix), + RoutePropertiesFormat: &network.RoutePropertiesFormat{}, + } + op, err := az.routeUpdater.addRouteOperation(routeOperationDelete, route) + if err != nil { + klog.Errorf("DeleteRoute failed for node %q with error: %v", kubeRoute.TargetNode, err) + return err + } + + // Wait for operation complete. + err = op.wait() + if err != nil { + klog.Errorf("DeleteRoute failed for node %q with error: %v", kubeRoute.TargetNode, err) + return err + } + } + klog.V(2).Infof("DeleteRoute: route deleted. clusterName=%q instance=%q cidr=%q", clusterName, kubeRoute.TargetNode, kubeRoute.DestinationCIDR) isOperationSucceeded = true diff --git a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes_test.go b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes_test.go index a85379410e5..1c0e7b96c77 100644 --- a/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes_test.go +++ b/staging/src/k8s.io/legacy-cloud-providers/azure/azure_routes_test.go @@ -741,3 +741,72 @@ func TestCleanupOutdatedRoutes(t *testing.T) { }) } } + +func TestDeleteRouteDualStack(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + routeTableClient := mockroutetableclient.NewMockInterface(ctrl) + + cloud := &Cloud{ + RouteTablesClient: routeTableClient, + Config: Config{ + RouteTableResourceGroup: "foo", + RouteTableName: "bar", + Location: "location", + }, + unmanagedNodes: sets.NewString(), + nodeInformerSynced: func() bool { return true }, + ipv6DualStackEnabled: true, + } + cache, _ := cloud.newRouteTableCache() + cloud.rtCache = cache + cloud.routeUpdater = newDelayedRouteUpdater(cloud, 100*time.Millisecond) + go cloud.routeUpdater.run() + + route := cloudprovider.Route{ + TargetNode: "node", + DestinationCIDR: "1.2.3.4/24", + } + routeName := mapNodeNameToRouteName(true, route.TargetNode, route.DestinationCIDR) + routeNameIPV4 := mapNodeNameToRouteName(false, route.TargetNode, route.DestinationCIDR) + routeTables := network.RouteTable{ + Name: &cloud.RouteTableName, + Location: &cloud.Location, + RouteTablePropertiesFormat: &network.RouteTablePropertiesFormat{ + Routes: &[]network.Route{ + { + Name: &routeName, + }, + { + Name: &routeNameIPV4, + }, + }, + }, + } + routeTablesAfterFirstDeletion := network.RouteTable{ + Name: &cloud.RouteTableName, + Location: &cloud.Location, + RouteTablePropertiesFormat: &network.RouteTablePropertiesFormat{ + Routes: &[]network.Route{ + { + Name: &routeNameIPV4, + }, + }, + }, + } + routeTablesAfterSecondDeletion := network.RouteTable{ + Name: &cloud.RouteTableName, + Location: &cloud.Location, + RouteTablePropertiesFormat: &network.RouteTablePropertiesFormat{ + Routes: &[]network.Route{}, + }, + } + routeTableClient.EXPECT().Get(gomock.Any(), cloud.RouteTableResourceGroup, cloud.RouteTableName, "").Return(routeTables, nil).AnyTimes() + routeTableClient.EXPECT().CreateOrUpdate(gomock.Any(), cloud.RouteTableResourceGroup, cloud.RouteTableName, routeTablesAfterFirstDeletion, "").Return(nil) + routeTableClient.EXPECT().CreateOrUpdate(gomock.Any(), cloud.RouteTableResourceGroup, cloud.RouteTableName, routeTablesAfterSecondDeletion, "").Return(nil) + err := cloud.DeleteRoute(context.TODO(), "cluster", &route) + if err != nil { + t.Errorf("unexpected error deleting route: %v", err) + t.FailNow() + } +}