Skip to content

feat: clean stale provisioner files #9545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Remove stale session directories
  • Loading branch information
mtojek committed Sep 6, 2023
commit afb0982d2ebca2bcf066b2defd0c27a014e65a7c
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ require (
tailscale.com v1.46.1
)

require gopkg.in/DataDog/dd-trace-go.v1 v1.54.0
require (
github.com/djherbis/atime v1.1.0
gopkg.in/DataDog/dd-trace-go.v1 v1.54.0
)

require (
cloud.google.com/go/compute v1.23.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw=
github.com/djherbis/atime v1.1.0 h1:rgwVbP/5by8BvvjBNrbh64Qz33idKT3pSnMSJsxhi0g=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
Expand Down
61 changes: 54 additions & 7 deletions provisionersdk/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ import (
"strings"
"time"

"github.com/djherbis/atime"
"github.com/google/uuid"
"golang.org/x/xerrors"

"cdr.dev/slog"
"github.com/coder/coder/v2/provisionersdk/proto"
)

// ReadmeFile is the location we look for to extract documentation from template
// versions.
const ReadmeFile = "README.md"
const (
// ReadmeFile is the location we look for to extract documentation from template versions.
ReadmeFile = "README.md"

sessionDirPrefix = "Session"
staleSessionDaysThreshold = 7 * 24 * time.Hour
)

// protoServer is a wrapper that translates the dRPC protocol into a Session with method calls into the Server.
type protoServer struct {
Expand All @@ -36,11 +41,13 @@ func (p *protoServer) Session(stream proto.DRPCProvisioner_SessionStream) error
server: p.server,
}

// TODO Clean stale sessions in WorkDir
err := cleanStaleSessions(s.Context(), p.opts.WorkDirectory, time.Now(), s.Logger)
if err != nil {
return xerrors.Errorf("clean state sessions %q: %w", s.WorkDirectory, err)
}

sessDir := fmt.Sprintf("Session%s", sessID)
s.WorkDirectory = filepath.Join(p.opts.WorkDirectory, sessDir)
err := os.MkdirAll(s.WorkDirectory, 0o700)
s.WorkDirectory = filepath.Join(p.opts.WorkDirectory, sessionDir(sessID))
err = os.MkdirAll(s.WorkDirectory, 0o700)
if err != nil {
return xerrors.Errorf("create work directory %q: %w", s.WorkDirectory, err)
}
Expand Down Expand Up @@ -319,3 +326,43 @@ func (r *request[R, C]) do() (C, error) {
return c, nil
}
}

func cleanStaleSessions(ctx context.Context, workDirectory string, now time.Time, logger slog.Logger) error {
entries, err := os.ReadDir(workDirectory)
if err != nil {
return xerrors.Errorf("can't read %q directory", workDirectory)
}

for _, entry := range entries {
dirName := entry.Name()

if entry.IsDir() && isValidSessionDir(dirName) {
sessionDirPath := filepath.Join(workDirectory, dirName)
fi, err := entry.Info()
if err != nil {
return xerrors.Errorf("can't read %q directory info", sessionDirPath)
}

lastAccessTime := atime.Get(fi)
if lastAccessTime.Add(staleSessionDaysThreshold).After(now) {
continue
}

logger.Info(ctx, "Remove stale session directory: %s", sessionDirPath)
err = os.RemoveAll(sessionDirPath)
if err != nil {
return xerrors.Errorf("can't remove %q directory", sessionDirPath)
}
}
}
return nil
}

func sessionDir(sessID string) string {
return sessionDirPrefix + sessID
}

func isValidSessionDir(dirName string) bool {
match, err := filepath.Match(sessionDirPrefix+"*", dirName)
return err == nil && match
}