Skip to content

Commit 2f4a643

Browse files
committed
Fix VLAN Leak
The logic was backwards so what was likely to happen was the network would be deleted, we'd update the allocation, which would then fail due to a conflict error. We'd never reconcile again because the network had gone and thus we had no way of getting the segmentation ID. This makes deallocation idempotent so it can be called multiple times, and does it before the network is deleted so we always have access to the segmentation ID.
1 parent c271292 commit 2f4a643

File tree

3 files changed

+21
-16
lines changed

3 files changed

+21
-16
lines changed

pkg/providers/allocation/vlan/vlan.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,16 @@ func (a *Allocator) Free(ctx context.Context, id int) error {
199199

200200
allocation.Spec.Allocations = slices.DeleteFunc(allocation.Spec.Allocations, callback)
201201

202-
// Question... would this make more sense to be idempotent?
203-
// Logic would dictate that some source of truth has this marked as allocated
204-
// but we think it isn't so it's probably a bug somewhere.
205-
if len(allocation.Spec.Allocations) != allocationsLength-1 {
202+
delta := allocationsLength - len(allocation.Spec.Allocations)
203+
204+
// Deallocation is idempotent.
205+
if delta == 0 {
206+
return nil
207+
}
208+
209+
// If we have more than one allocation, a bug has crept in or someone has
210+
// manually corrupted the allocations table.
211+
if delta > 1 {
206212
return fmt.Errorf("%w: vlan id %d not allocated exactly once", ErrAllocation, id)
207213
}
208214

pkg/providers/allocation/vlan/vlan_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func TestVLANAllocationIllegalRangeHigh(t *testing.T) {
177177
require.ErrorIs(t, err, vlan.ErrAllocation)
178178
}
179179

180-
// TestVLANFree tests an allocated VLAN can be freed, and not freed multiple times.
180+
// TestVLANFree tests an allocated VLAN can be freed, and is idempotent.
181181
func TestVLANFree(t *testing.T) {
182182
t.Parallel()
183183

@@ -190,7 +190,7 @@ func TestVLANFree(t *testing.T) {
190190
require.Equal(t, segmentStart, id)
191191

192192
require.NoError(t, allocator.Free(t.Context(), id))
193-
require.ErrorIs(t, allocator.Free(t.Context(), id), vlan.ErrAllocation)
193+
require.NoError(t, allocator.Free(t.Context(), id))
194194
}
195195

196196
// TestVLANFreeIllegalID tests illegaly VLAN IDs are caught.

pkg/providers/internal/openstack/provider.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,26 +1774,25 @@ func (p *Provider) DeleteNetwork(ctx context.Context, identity *unikornv1.Identi
17741774
if openstackNetwork != nil {
17751775
log.V(1).Info("deleting network")
17761776

1777-
if err := networking.DeleteNetwork(ctx, openstackNetwork.ID); err != nil {
1778-
return err
1779-
}
1780-
1777+
// VLAN deallocation is idempotent, but requires the network to
1778+
// exist so we can lookup the segmentation ID, so this has to
1779+
// occur first.
17811780
if p.region.Spec.Openstack.Network.UseProviderNetworks() {
17821781
log.V(1).Info("freeing vlan", "id", openstackNetwork.SegmentationID)
17831782

17841783
vlanID, err := strconv.Atoi(openstackNetwork.SegmentationID)
17851784
if err != nil {
1786-
log.Error(err, "failed to free vlan", "id", vlanID)
1787-
1788-
return nil
1785+
return fmt.Errorf("%w: segmentation ID not parsable", err)
17891786
}
17901787

17911788
if err := p.vlanAllocator.Free(ctx, vlanID); err != nil {
1792-
log.Error(err, "failed to free vlan", "id", vlanID)
1793-
1794-
return nil
1789+
return fmt.Errorf("%w: failed to free vlan", err)
17951790
}
17961791
}
1792+
1793+
if err := networking.DeleteNetwork(ctx, openstackNetwork.ID); err != nil {
1794+
return err
1795+
}
17971796
}
17981797

17991798
return nil

0 commit comments

Comments
 (0)