Skip to content
Merged
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
9 changes: 9 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
version: "2"

linters:
enable:
- exhaustive

disable:
- errcheck

default: standard
settings:
exhaustive:
# A bare `default:` does not by itself make a switch exhaustive; every
# enum member must be listed (use a `//exhaustive:ignore` directive to
# opt a specific switch out).
default-signifies-exhaustive: false
exclusions:
paths:
- "dataset/"
Expand Down
6 changes: 6 additions & 0 deletions appconfig/configerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ func walkAST(p *tomlast.Parser, parts []string, depth int) int {
shape := p.Shape(keyNode.Raw)
return shape.Start.Line
}

case tomlast.Invalid, tomlast.Comment, tomlast.Key, tomlast.Array,
tomlast.InlineTable, tomlast.String, tomlast.Bool, tomlast.Float,
tomlast.Integer, tomlast.LocalDate, tomlast.LocalTime,
tomlast.LocalDateTime, tomlast.DateTime:
// Not top-level expressions; keep scanning.
}
}

Expand Down
1 change: 1 addition & 0 deletions cli/commands/deploy_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func (m *deployInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

switch msg := msg.(type) {
case tea.KeyMsg:
//exhaustive:ignore tea.KeyType has ~90 members; default handles the rest
switch msg.Type {
case tea.KeyCtrlC:
m.interrupted = true
Expand Down
3 changes: 3 additions & 0 deletions components/coordinate/advertise.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ func ComputeAdvertise(in AdvertiseInput) ([]AdvertiseCandidate, []string) {
case netcheckUnreachable:
cand.Included = false
cand.Reason = "address family proven unreachable by netcheck"
case netcheckNotRun:
// No netcheck result yet; keep the candidate.
fallthrough
default:
cand.Included = true
cand.Reason = "no netcheck override"
Expand Down
6 changes: 6 additions & 0 deletions components/diskio/disk_mount_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ func (c *DiskMountController) reconcileMountMounted(ctx context.Context, mount *
case storage_v1alpha.DM_DETACHED:
c.log.Info("mount detached but desired mounted, recovering", "entity_id", entityId)
return c.attachAndMount(ctx, mount)
case storage_v1alpha.DM_UNMOUNTING, storage_v1alpha.DM_DETACHING:
// Tearing down while desired state is mounted; unexpected.
fallthrough
default:
c.log.Warn("unexpected actual state for mounted", "actual_state", mount.ActualState)
return nil
Expand All @@ -187,6 +190,9 @@ func (c *DiskMountController) reconcileMountUnmounted(ctx context.Context, mount
return nil
case storage_v1alpha.DM_UNMOUNTING, storage_v1alpha.DM_DETACHING:
return nil
case storage_v1alpha.DM_PENDING, storage_v1alpha.DM_ATTACHING, storage_v1alpha.DM_ATTACHED, storage_v1alpha.DM_MOUNTING, storage_v1alpha.DM_MOUNTED, storage_v1alpha.DM_ERROR:
// Still attached/mounted; tear it down to reach the unmounted state.
fallthrough
default:
return c.unmountAndDetach(ctx, mount)
}
Expand Down
6 changes: 6 additions & 0 deletions components/diskio/disk_volume_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ func (c *DiskVolumeController) reconcileVolumePresent(ctx context.Context, volum
case storage_v1alpha.DV_ERROR:
c.log.Info("volume in error state, attempting recreation", "entity_id", entityId)
return c.createVolume(ctx, volume)
case storage_v1alpha.DV_DELETING, storage_v1alpha.DV_DELETED:
// Volume is being torn down while desired state is present; unexpected.
fallthrough
default:
c.log.Warn("unexpected actual state for present volume", "actual_state", volume.ActualState)
return nil
Expand All @@ -394,6 +397,9 @@ func (c *DiskVolumeController) reconcileVolumeAbsent(ctx context.Context, volume
return nil
case storage_v1alpha.DV_DELETING:
return nil
case storage_v1alpha.DV_PENDING, storage_v1alpha.DV_CREATING, storage_v1alpha.DV_READY, storage_v1alpha.DV_ERROR:
// Volume still exists; delete it to reach the absent state.
fallthrough
default:
return c.deleteVolume(ctx, volume)
}
Expand Down
20 changes: 20 additions & 0 deletions components/ipalloc/ipalloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,31 @@ func (a *Allocator) Watch(ctx context.Context, eac *entityserver_v1alpha.EntityA
if err := a.assignService(ctx, ev.Entity, eac); err != nil {
a.log.Error("failed to assign service", "error", err, "service", ev.Id)
}
case indexwatch.EventDeleted:
// Service removed; return its reserved IPs to the pool.
a.releaseServiceAllocations(ev.Id)
}
}
}
}

// releaseServiceAllocations returns every IP reserved for the given service to
// the pool. The allocation table is otherwise append-only, so without this a
// deleted service would hold its addresses forever and slowly exhaust the
// subnet.
func (a *Allocator) releaseServiceAllocations(id entity.Id) {
owner := id.String()

a.mu.Lock()
defer a.mu.Unlock()

for addr, holder := range a.allocations {
if holder == owner {
delete(a.allocations, addr)
}
}
}

type service struct {
network_v1alpha.Service
*entity.Entity
Expand Down
46 changes: 46 additions & 0 deletions components/ipalloc/ipalloc_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package ipalloc

import (
"context"
"io"
"log/slog"
"net/netip"
"testing"

"github.com/stretchr/testify/require"
"miren.dev/runtime/pkg/entity"
)

func TestAllocator_random(t *testing.T) {
Expand Down Expand Up @@ -48,3 +52,45 @@ func TestAllocator_random(t *testing.T) {
r.Greater(len(seen), 900)
})
}

func TestAllocator_releaseServiceAllocations(t *testing.T) {
r := require.New(t)

log := slog.New(slog.NewTextHandler(io.Discard, nil))
a := NewAllocator(log, []netip.Prefix{
netip.MustParsePrefix("10.10.0.0/16"),
})

svcA := entity.Id("svc/a")
svcB := entity.Id("svc/b")

ipsA, err := a.Allocate(context.Background(), svcA)
r.NoError(err)
r.NotEmpty(ipsA)

ipsB, err := a.Allocate(context.Background(), svcB)
r.NoError(err)
r.NotEmpty(ipsB)

// Both services hold their reservations.
r.Len(a.allocations, len(ipsA)+len(ipsB))

// Releasing A returns only A's addresses to the pool.
a.releaseServiceAllocations(svcA)

for _, ip := range ipsA {
_, ok := a.allocations[ip]
r.Falsef(ok, "expected %s to be released", ip)
}
for _, ip := range ipsB {
holder, ok := a.allocations[ip]
r.Truef(ok, "expected %s to remain reserved", ip)
r.Equal(svcB.String(), holder)
}
r.Len(a.allocations, len(ipsB))

// A's freed addresses can be handed out again.
ipsA2, err := a.Allocate(context.Background(), svcA)
r.NoError(err)
r.NotEmpty(ipsA2)
}
6 changes: 6 additions & 0 deletions controllers/deployment/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,9 @@ func (l *Launcher) ensureServiceForPorts(ctx context.Context, app *core_v1alpha.
switch p.Protocol {
case core_v1alpha.ConfigSpecServicesPortsUDP:
np.Protocol = network_v1alpha.UDP
case core_v1alpha.ConfigSpecServicesPortsTCP:
// TCP is also the default for an unspecified protocol.
fallthrough
default:
np.Protocol = network_v1alpha.TCP
}
Expand Down Expand Up @@ -1695,6 +1698,9 @@ func (l *Launcher) hasActiveSandboxForPool(ctx context.Context, poolID entity.Id
switch sb.Status {
case compute_v1alpha.RUNNING, compute_v1alpha.PENDING, compute_v1alpha.NOT_READY:
// Active — may still hold disk resources
case compute_v1alpha.STOPPED, compute_v1alpha.DEAD:
// Terminal — no longer holds resources.
fallthrough
default:
continue
}
Expand Down
1 change: 1 addition & 0 deletions controllers/deployment/specs_match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
func structFingerprint(t reflect.Type) string {
var walk func(reflect.Type) string
walk = func(t reflect.Type) string {
//exhaustive:ignore reflect.Kind has ~27 members; default handles the rest
switch t.Kind() {
case reflect.Slice, reflect.Array, reflect.Pointer:
return t.Kind().String() + "<" + walk(t.Elem()) + ">"
Expand Down
6 changes: 6 additions & 0 deletions controllers/disk/disk_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ func (d *DiskController) handleProvisioning(ctx context.Context, disk *storage_v
"error", existingVolume.ErrorMessage)
return nil

case storage_v1alpha.DV_PENDING, storage_v1alpha.DV_CREATING, storage_v1alpha.DV_DELETING, storage_v1alpha.DV_DELETED:
// Not yet ready; wait for the volume to settle.
fallthrough
default:
d.Log.Debug("disk_volume still provisioning",
"disk", disk.ID,
Expand Down Expand Up @@ -445,6 +448,9 @@ func diskModeToVolumeMode(mode storage_v1alpha.DiskMode) storage_v1alpha.DiskVol
switch mode {
case storage_v1alpha.ACCELERATOR:
return storage_v1alpha.VM_ACCELERATOR
case storage_v1alpha.UNIVERSAL:
// Universal is also the default for an unspecified mode.
fallthrough
default:
return storage_v1alpha.VM_UNIVERSAL
}
Expand Down
3 changes: 3 additions & 0 deletions controllers/disk/disk_lease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ func (d *DiskLeaseController) handlePendingLease(ctx context.Context, lease *sto
}
// Fall through to create a new mount entity

case storage_v1alpha.DM_PENDING, storage_v1alpha.DM_ATTACHING, storage_v1alpha.DM_ATTACHED, storage_v1alpha.DM_MOUNTING, storage_v1alpha.DM_UNMOUNTING, storage_v1alpha.DM_DETACHING:
// Mount lifecycle still in progress; wait for it to settle.
fallthrough
default:
d.Log.Debug("disk_mount still in progress",
"lease", leaseId,
Expand Down
6 changes: 6 additions & 0 deletions controllers/integration/chaos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ func (r *chaosReport) collectEntityStats(t *testing.T, ctx context.Context, h *T
r.provisionedDisks++
case storage.ERROR:
r.errorDisks++
case storage.PROVISIONING, storage.ATTACHED, storage.DETACHED, storage.DELETING, storage.RESTORING:
// Counted together as "other".
fallthrough
default:
r.otherDisks++
}
Expand All @@ -108,6 +111,9 @@ func (r *chaosReport) collectEntityStats(t *testing.T, ctx context.Context, h *T
r.mountedMounts++
case storage.DM_DETACHED:
r.detachedMounts++
case storage.DM_PENDING, storage.DM_ATTACHING, storage.DM_ATTACHED, storage.DM_MOUNTING, storage.DM_UNMOUNTING, storage.DM_DETACHING, storage.DM_ERROR:
// Counted together as "other".
fallthrough
default:
r.otherMounts++
}
Expand Down
3 changes: 2 additions & 1 deletion controllers/sandbox/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ func (s *SandboxLogs) scanJSON(line string) (string, observability.LogStream, bo
// Raw preserves the original numeric literal (large integers
// included) and renders bools as "true"/"false".
s.extra[k] = value.Raw
case gjson.Null, gjson.JSON:
// Null and nested objects/arrays are skipped.
}
// gjson.Null and nested objects/arrays (gjson.JSON) are skipped.
}
return true
})
Expand Down
3 changes: 3 additions & 0 deletions controllers/sandbox/saga_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ func (s *SagaSandboxController) Create(ctx context.Context, co *compute.Sandbox,
}

return s.createSandboxViaSaga(ctx, co)
case compute.NOT_READY:
// Transient boot state; nothing to reconcile until it resolves.
fallthrough
default:
s.log.Warn("ignoring sandbox status", "status", co.Status)
return nil
Expand Down
10 changes: 9 additions & 1 deletion controllers/sandbox/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ func (c *SandboxController) SetPortStatus(id string, port observability.BoundPor
ports.Ports = slices.DeleteFunc(ports.Ports, func(p observability.BoundPort) bool {
return p == port
})
case observability.PortStatusActive:
// Liveness signal only; does not change the bound set.
}

c.portCond.Broadcast()
Expand Down Expand Up @@ -721,8 +723,11 @@ func (c *SandboxController) isContainerHealthy(ctx context.Context, containerID
// We don't expect paused sandboxes in normal operation
c.Log.Debug("task in paused/pausing state, marking unhealthy", "id", containerID, "status", status.Status)
return false
case containerd.Unknown:
// Unknown status is unhealthy.
fallthrough
default:
// Unknown or any other status is unhealthy
// Any other status is unhealthy
c.Log.Debug("task in unknown/unhealthy state", "id", containerID, "status", status.Status)
return false
}
Expand Down Expand Up @@ -951,6 +956,9 @@ func (c *SandboxController) Create(ctx context.Context, co *compute.Sandbox, met
}

return c.createSandbox(ctx, co, meta, false)
case compute.NOT_READY:
// Transient boot state; nothing to reconcile until it resolves.
fallthrough
default:
c.Log.Warn("ignoring sandbox status", "status", co.Status)
return nil
Expand Down
4 changes: 2 additions & 2 deletions controllers/sandbox/sandbox_frozen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
// sha256sum controllers/sandbox/sandbox.go controllers/sandbox/volume.go controllers/sandbox/firewall.go
func TestSandboxControllerFrozen(t *testing.T) {
frozen := map[string]string{
"sandbox.go": "bc84259d85ac797bbf7d031cef36b32294183ac082ea4a0fd76b3da730af82bf",
"volume.go": "292dbc050cd94901ab704a23605f5537c944787c9e06077a3fc004f40e9c0b6c",
"sandbox.go": "9fbee5834397f3600e9706fbe78fad45e69b0fa7bc5908afb3d887ffe8fa3ef7",
"volume.go": "b4697764d48a90adc04ce47968ccef11ceba50da8d19c889906c5c3a539065b3",
"firewall.go": "648cb5d91091d5eb7400152b19695a8045585feae59c5dd36c12d663a27bb91f",
}

Expand Down
3 changes: 3 additions & 0 deletions controllers/sandbox/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ func (c *SandboxController) waitForLeaseBound(ctx context.Context, leaseID entit
// Still pending, continue waiting
continue

case storage.RELEASED:
// Lease released out from under us while waiting to bind.
fallthrough
default:
return "", fmt.Errorf("unexpected disk lease status: %s", lease.Status)
}
Expand Down
4 changes: 4 additions & 0 deletions controllers/sandboxpool/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func TestManagerScaleUpPartial(t *testing.T) {
running++
case compute_v1alpha.PENDING:
pending++
case compute_v1alpha.NOT_READY, compute_v1alpha.STOPPED, compute_v1alpha.DEAD:
// Not counted in this assertion.
}
}

Expand Down Expand Up @@ -564,6 +566,8 @@ func TestManagerScaleDownFixedModeProactive(t *testing.T) {
runningCount++
case compute_v1alpha.STOPPED:
stoppedCount++
case compute_v1alpha.PENDING, compute_v1alpha.NOT_READY, compute_v1alpha.DEAD:
// Not counted in this assertion.
}
}

Expand Down
3 changes: 2 additions & 1 deletion controllers/service/gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,9 @@ func (s *ServiceController) applyGC(ctx context.Context, target *targetState, ac
if !target.chains.Contains(chain) {
orphanLeaves = append(orphanLeaves, chain)
}
case chainKindStatic, chainKindUnknown:
// Leave alone.
}
// chainKindStatic and chainKindUnknown: leave alone.
}
orphanParentCount := len(orphanNodePorts) + len(orphanServices)

Expand Down
2 changes: 2 additions & 0 deletions lsvd/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,8 @@ func (d *Disk) checkFlush(ctx context.Context) error {
}

switch reason {
case FlushNo:
// Already handled by the early return above; unreachable here.
case FlushTime:
d.log.Info("flushing segment due to maximum segment lifetime",
"age", time.Since(d.curOC.builder.openedAt),
Expand Down
3 changes: 3 additions & 0 deletions lsvd/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,9 @@ func appendTextValue(s *handleState, v slog.Value) error {
return nil
}
s.appendString(fmt.Sprintf("%+v", v.Any()))
case slog.KindBool, slog.KindDuration, slog.KindFloat64, slog.KindInt64, slog.KindUint64, slog.KindGroup, slog.KindLogValuer:
// Defer to the generic value formatter.
fallthrough
default:
*s.buf = appendValue(v, *s.buf)
}
Expand Down
2 changes: 2 additions & 0 deletions lsvd/torture.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ func (g *TortureGenerator) Next() TortureOperation {
case TortureOpZero:
op.Extent = g.nextExtent(true)
g.lastWrite = op.Extent
case TortureOpSync, TortureOpCloseReopen:
// Whole-disk operations; no extent or data to populate.
}

return op
Expand Down
Loading
Loading