CleanStaleSessions browses the work directory searching for stale session directories. Coder provisioner is supposed to remove them once after finishing the provisioning, but there is a risk of keeping them in case of a failure.
(ctx context.Context, logger slog.Logger, fs afero.Fs, now time.Time)
| 215 | // directories. Coder provisioner is supposed to remove them once after finishing the provisioning, |
| 216 | // but there is a risk of keeping them in case of a failure. |
| 217 | func (l Layout) CleanStaleSessions(ctx context.Context, logger slog.Logger, fs afero.Fs, now time.Time) error { |
| 218 | parent := filepath.Dir(l.WorkDirectory()) |
| 219 | entries, err := afero.ReadDir(fs, filepath.Dir(l.WorkDirectory())) |
| 220 | if err != nil { |
| 221 | return xerrors.Errorf("can't read %q directory", parent) |
| 222 | } |
| 223 | |
| 224 | for _, fi := range entries { |
| 225 | dirName := fi.Name() |
| 226 | |
| 227 | if fi.IsDir() && isValidSessionDir(dirName) { |
| 228 | sessionDirPath := filepath.Join(parent, dirName) |
| 229 | |
| 230 | modTime := fi.ModTime() // fallback to modTime if modTime is not available (afero) |
| 231 | |
| 232 | if modTime.Add(staleSessionRetention).After(now) { |
| 233 | continue |
| 234 | } |
| 235 | |
| 236 | logger.Info(ctx, "remove stale session directory", slog.F("session_path", sessionDirPath)) |
| 237 | err = fs.RemoveAll(sessionDirPath) |
| 238 | if err != nil { |
| 239 | // This should not be a fatal error. If it is, the provisioner would be rendered |
| 240 | // non-functional until this directory is cleaned up. Ideally there would be a |
| 241 | // way to escalate this to an operator alert in Coder. Until then, the best we |
| 242 | // can do is log it on every cleanup attempt (every build). Eventually the disk |
| 243 | // usage will be noticeable, and hopefully these logs are noticed. |
| 244 | logger.Error(ctx, "failed to remove stale session directory", |
| 245 | slog.F("directory", sessionDirPath), |
| 246 | slog.Error(err), |
| 247 | ) |
| 248 | |
| 249 | if l.WorkDirectory() == sessionDirPath { |
| 250 | // This should never happen because sessions are uuid's. But if that logic ever |
| 251 | // changes, this would be a bad state to be in. The directory that the |
| 252 | // provisioner is going to use cannot be stale. |
| 253 | return xerrors.Errorf("remove %q directory, will not work inside a stale directory: %w", sessionDirPath, err) |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | return nil |
| 259 | } |