mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
7642e5af98
* fix(scheduler): data race when pushing new tasks the problem here is that scheduler can be closed in two ways: - canceling the context given as argument to scheduler.RunScheduler() - running scheduler.Shutdown() because of this shutdown can trigger a data race between calling scheduler.inShutdown() and actually pushing tasks into the pool workers solved that by keeping a quit channel and listening on both quit channel and ctx.Done() and closing the worker chan and scheduler afterwards. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> * refactor(scheduler): refactor into a single shutdown before this we could stop scheduler either by closing the context provided to RunScheduler(ctx) or by running Shutdown(). simplify things by getting rid of the external context in RunScheduler(). keep an internal context in the scheduler itself and pass it down to all tasks. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> --------- Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
171 lines
3.4 KiB
Go
171 lines
3.4 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gorilla/securecookie"
|
|
"github.com/gorilla/sessions"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
"zotregistry.io/zot/pkg/scheduler"
|
|
"zotregistry.io/zot/pkg/storage"
|
|
storageConstants "zotregistry.io/zot/pkg/storage/constants"
|
|
)
|
|
|
|
const cookiesMaxAge = 7200 // 2h
|
|
|
|
type CookieStore struct {
|
|
needsCleanup bool // if store should be periodically cleaned
|
|
rootDir string
|
|
sessions.Store
|
|
}
|
|
|
|
func (c *CookieStore) RunSessionCleaner(sch *scheduler.Scheduler) {
|
|
if c.needsCleanup {
|
|
sch.SubmitGenerator(&SessionCleanup{rootDir: c.rootDir}, cookiesMaxAge, scheduler.LowPriority)
|
|
}
|
|
}
|
|
|
|
func NewCookieStore(storeController storage.StoreController) (*CookieStore, error) {
|
|
// To store custom types in our cookies
|
|
// we must first register them using gob.Register
|
|
gob.Register(map[string]interface{}{})
|
|
|
|
hashKey, err := getHashKey()
|
|
if err != nil {
|
|
return &CookieStore{}, err
|
|
}
|
|
|
|
var store sessions.Store
|
|
|
|
var sessionsDir string
|
|
|
|
var needsCleanup bool
|
|
|
|
if storeController.DefaultStore.Name() == storageConstants.LocalStorageDriverName {
|
|
sessionsDir = path.Join(storeController.DefaultStore.RootDir(), "_sessions")
|
|
if err := os.MkdirAll(sessionsDir, storageConstants.DefaultDirPerms); err != nil {
|
|
return &CookieStore{}, err
|
|
}
|
|
|
|
localStore := sessions.NewFilesystemStore(sessionsDir, hashKey)
|
|
|
|
localStore.MaxAge(cookiesMaxAge)
|
|
|
|
store = localStore
|
|
needsCleanup = true
|
|
} else {
|
|
memStore := sessions.NewCookieStore(hashKey)
|
|
|
|
memStore.MaxAge(cookiesMaxAge)
|
|
|
|
store = memStore
|
|
}
|
|
|
|
return &CookieStore{
|
|
Store: store,
|
|
rootDir: sessionsDir,
|
|
needsCleanup: needsCleanup,
|
|
}, nil
|
|
}
|
|
|
|
func getHashKey() ([]byte, error) {
|
|
hashKey := securecookie.GenerateRandomKey(64)
|
|
if hashKey == nil {
|
|
return nil, zerr.ErrHashKeyNotCreated
|
|
}
|
|
|
|
return hashKey, nil
|
|
}
|
|
|
|
func getExpiredSessions(dir string) ([]string, error) {
|
|
sessions := make([]string, 0)
|
|
|
|
err := filepath.WalkDir(dir, func(_ string, dirEntry fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fileInfo, err := dirEntry.Info()
|
|
if err != nil { // may have been deleted in the meantime
|
|
return nil //nolint: nilerr
|
|
}
|
|
|
|
if !strings.HasPrefix(fileInfo.Name(), "session_") {
|
|
return nil
|
|
}
|
|
|
|
if fileInfo.ModTime().Add(cookiesMaxAge * time.Second).Before(time.Now()) {
|
|
sessions = append(sessions, path.Join(dir, fileInfo.Name()))
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return sessions, err
|
|
}
|
|
|
|
type SessionCleanup struct {
|
|
rootDir string
|
|
done bool
|
|
}
|
|
|
|
func (gen *SessionCleanup) Next() (scheduler.Task, error) {
|
|
sessions, err := getExpiredSessions(gen.rootDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(sessions) == 0 {
|
|
gen.done = true
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
return &CleanTask{sessions: sessions}, nil
|
|
}
|
|
|
|
func (gen *SessionCleanup) IsDone() bool {
|
|
return gen.done
|
|
}
|
|
|
|
func (gen *SessionCleanup) IsReady() bool {
|
|
return true
|
|
}
|
|
|
|
func (gen *SessionCleanup) Reset() {
|
|
gen.done = false
|
|
}
|
|
|
|
type CleanTask struct {
|
|
sessions []string
|
|
}
|
|
|
|
func (cleanTask *CleanTask) DoWork(ctx context.Context) error {
|
|
for _, session := range cleanTask.sessions {
|
|
if err := os.Remove(session); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (cleanTask *CleanTask) String() string {
|
|
return fmt.Sprintf("{Name: %s, sessions: %s}",
|
|
cleanTask.Name(), cleanTask.sessions)
|
|
}
|
|
|
|
func (cleanTask *CleanTask) Name() string {
|
|
return "SessionCleanupTask"
|
|
}
|