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
2 changes: 1 addition & 1 deletion .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ local bootstrap = '25.02';
local nginx = '1.24.0';
local python = '3.12-slim-bookworm';
local alpine = '3.21';
local visual_diff_skip_build = '2786';
local visual_diff_skip_build = '2837';

local build(arch, testUI) = [{
kind: 'pipeline',
Expand Down
29 changes: 10 additions & 19 deletions backend/cmd/stability/main.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
package main

import (
"context"
"os"
"os/signal"
"path"
"syscall"

"github.com/syncloud/platform/hook"
"github.com/syncloud/platform/log"
"github.com/syncloud/platform/stability"
)

func main() {
logger := log.Default()
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()

mem := stability.NewMemInfo("/proc")
commonDir := os.Getenv("SNAP_COMMON")
if commonDir == "" {
commonDir = "/var/snap/platform/common"
}
events := stability.NewEventLog(commonDir + "/stability-events.jsonl")
z := stability.NewZram(mem, stability.SwaponSyscall, stability.SwapoffSyscall, events, logger)
if err := z.EnsureConfigured(); err != nil {
eventsPath := path.Join(hook.DataDir, "stability-events.jsonl")
stability.MigrateEventLog(path.Join(hook.CommonDir, "stability-events.jsonl"), eventsPath, logger)
events := stability.NewEventLog(eventsPath)
zram := stability.NewZram(mem, stability.SwaponSyscall, stability.SwapoffSyscall, events, logger)
if err := zram.EnsureConfigured(); err != nil {
logger.Sugar().Warnf("stability: zram setup failed (continuing): %v", err)
}

scan := stability.NewProcScanner("/proc")
w := stability.NewWatcher(mem, scan, func(pid int, sig syscall.Signal) error {
scanner := stability.NewProcScanner("/proc")
watcher := stability.NewWatcher(mem, scanner, func(pid int, sig syscall.Signal) error {
return syscall.Kill(pid, sig)
}, events, logger)

if err := w.Run(ctx); err != nil && err != context.Canceled {
logger.Sugar().Errorf("stability: watcher exited: %v", err)
os.Exit(1)
}
watcher.Run()
}
4 changes: 2 additions & 2 deletions backend/ioc/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,8 @@ func Init(userConfig string, systemConfig string, backupDir string, varDir strin
return nil, err
}

err = c.Singleton(func() *stability.EventLog {
return stability.NewEventLog("/var/snap/platform/common/stability-events.jsonl")
err = c.Singleton(func(systemConfig *config.SystemConfig) *stability.EventLog {
return stability.NewEventLog(path.Join(systemConfig.DataDir(), "stability-events.jsonl"))
})
if err != nil {
return nil, err
Expand Down
25 changes: 25 additions & 0 deletions backend/stability/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package stability

import (
"errors"
"os"

"go.uber.org/zap"
)

func MigrateEventLog(oldPath, newPath string, logger *zap.Logger) {
if _, err := os.Stat(newPath); err == nil {
return
} else if !errors.Is(err, os.ErrNotExist) {
logger.Warn("stability: stat new event log failed", zap.Error(err))
return
}
if _, err := os.Stat(oldPath); err != nil {
return
}
if err := os.Rename(oldPath, newPath); err != nil {
logger.Warn("stability: migrate event log failed", zap.String("from", oldPath), zap.String("to", newPath), zap.Error(err))
return
}
logger.Info("stability: migrated event log", zap.String("from", oldPath), zap.String("to", newPath))
}
54 changes: 54 additions & 0 deletions backend/stability/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package stability

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)

func TestMigrateMovesWhenOnlyOldExists(t *testing.T) {
dir := t.TempDir()
old := filepath.Join(dir, "common", "events.jsonl")
newP := filepath.Join(dir, "data", "events.jsonl")
require.NoError(t, os.MkdirAll(filepath.Dir(old), 0755))
require.NoError(t, os.MkdirAll(filepath.Dir(newP), 0755))
require.NoError(t, os.WriteFile(old, []byte("{\"kind\":\"x\"}\n"), 0644))

MigrateEventLog(old, newP, zap.NewNop())

_, errOld := os.Stat(old)
assert.True(t, os.IsNotExist(errOld))
body, err := os.ReadFile(newP)
require.NoError(t, err)
assert.Equal(t, "{\"kind\":\"x\"}\n", string(body))
}

func TestMigrateSkipsWhenNewAlreadyExists(t *testing.T) {
dir := t.TempDir()
old := filepath.Join(dir, "events.jsonl")
newP := filepath.Join(dir, "new.jsonl")
require.NoError(t, os.WriteFile(old, []byte("old"), 0644))
require.NoError(t, os.WriteFile(newP, []byte("new"), 0644))

MigrateEventLog(old, newP, zap.NewNop())

oldBody, _ := os.ReadFile(old)
newBody, _ := os.ReadFile(newP)
assert.Equal(t, "old", string(oldBody), "old file untouched if new exists")
assert.Equal(t, "new", string(newBody), "new file untouched if new exists")
}

func TestMigrateNoopWhenOldMissing(t *testing.T) {
dir := t.TempDir()
old := filepath.Join(dir, "missing.jsonl")
newP := filepath.Join(dir, "new.jsonl")

MigrateEventLog(old, newP, zap.NewNop())

_, err := os.Stat(newP)
assert.True(t, os.IsNotExist(err))
}
14 changes: 4 additions & 10 deletions backend/stability/oom.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package stability

import (
"context"
"errors"
"os"
"syscall"
Expand Down Expand Up @@ -42,22 +41,17 @@ func NewWatcher(mem *MemInfo, scan *ProcScanner, kill KillFn, events *EventLog,
}
}

func (w *Watcher) Run(ctx context.Context) error {
func (w *Watcher) Run() {
t := time.NewTicker(w.interval)
defer t.Stop()
w.log.Info("oom-watcher: started",
zap.Duration("interval", w.interval),
zap.Float64("avail_min", w.availMin),
zap.Float64("psi_max", w.psiMax),
)
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-t.C:
if err := w.tick(); err != nil {
w.log.Warn("oom-watcher: tick error", zap.Error(err))
}
for range t.C {
if err := w.tick(); err != nil {
w.log.Warn("oom-watcher: tick error", zap.Error(err))
}
}
}
Expand Down