1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

projectquota: sync next projectID across Control instances

Signed-off-by: Timo Rothenpieler <timo@rothenpieler.org>
This commit is contained in:
Timo Rothenpieler 2020-08-09 19:52:20 +00:00
parent 1a5b7f50bc
commit 31ed121cb8
2 changed files with 82 additions and 18 deletions

View file

@ -55,6 +55,7 @@ import (
"io/ioutil" "io/ioutil"
"path" "path"
"path/filepath" "path/filepath"
"sync"
"unsafe" "unsafe"
"github.com/containerd/containerd/sys" "github.com/containerd/containerd/sys"
@ -63,6 +64,33 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
type pquotaState struct {
sync.Mutex
nextProjectID uint32
}
var pquotaStateInst *pquotaState
var pquotaStateOnce sync.Once
// getPquotaState - get global pquota state tracker instance
func getPquotaState() *pquotaState {
pquotaStateOnce.Do(func() {
pquotaStateInst = &pquotaState{
nextProjectID: 1,
}
})
return pquotaStateInst
}
// registerBasePath - register a new base path and update nextProjectID
func (state *pquotaState) updateMinProjID(minProjectID uint32) {
state.Lock()
defer state.Unlock()
if state.nextProjectID <= minProjectID {
state.nextProjectID = minProjectID + 1
}
}
// NewControl - initialize project quota support. // NewControl - initialize project quota support.
// Test to make sure that quota can be set on a test dir and find // Test to make sure that quota can be set on a test dir and find
// the first project id to be used for the next container create. // the first project id to be used for the next container create.
@ -115,11 +143,11 @@ func NewControl(basePath string) (*Control, error) {
// //
// Get project id of parent dir as minimal id to be used by driver // Get project id of parent dir as minimal id to be used by driver
// //
minProjectID, err := getProjectID(basePath) baseProjectID, err := getProjectID(basePath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
minProjectID++ minProjectID := baseProjectID + 1
// //
// Test if filesystem supports project quotas by trying to set // Test if filesystem supports project quotas by trying to set
@ -134,19 +162,24 @@ func NewControl(basePath string) (*Control, error) {
q := Control{ q := Control{
backingFsBlockDev: backingFsBlockDev, backingFsBlockDev: backingFsBlockDev,
nextProjectID: minProjectID + 1,
quotas: make(map[string]uint32), quotas: make(map[string]uint32),
} }
//
// update minimum project ID
//
state := getPquotaState()
state.updateMinProjID(minProjectID)
// //
// get first project id to be used for next container // get first project id to be used for next container
// //
err = q.findNextProjectID(basePath) err = q.findNextProjectID(basePath, baseProjectID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
logrus.Debugf("NewControl(%s): nextProjectID = %d", basePath, q.nextProjectID) logrus.Debugf("NewControl(%s): nextProjectID = %d", basePath, state.nextProjectID)
return &q, nil return &q, nil
} }
@ -157,19 +190,24 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error {
projectID, ok := q.quotas[targetPath] projectID, ok := q.quotas[targetPath]
q.RUnlock() q.RUnlock()
if !ok { if !ok {
q.Lock() state := getPquotaState()
projectID = q.nextProjectID state.Lock()
projectID = state.nextProjectID
// //
// assign project id to new container directory // assign project id to new container directory
// //
err := setProjectID(targetPath, projectID) err := setProjectID(targetPath, projectID)
if err != nil { if err != nil {
q.Unlock() state.Unlock()
return err return err
} }
state.nextProjectID++
state.Unlock()
q.Lock()
q.quotas[targetPath] = projectID q.quotas[targetPath] = projectID
q.nextProjectID++
q.Unlock() q.Unlock()
} }
@ -279,9 +317,25 @@ func setProjectID(targetPath string, projectID uint32) error {
// findNextProjectID - find the next project id to be used for containers // findNextProjectID - find the next project id to be used for containers
// by scanning driver home directory to find used project ids // by scanning driver home directory to find used project ids
func (q *Control) findNextProjectID(home string) error { func (q *Control) findNextProjectID(home string, baseID uint32) error {
q.Lock() state := getPquotaState()
defer q.Unlock() state.Lock()
defer state.Unlock()
checkProjID := func(path string) (uint32, error) {
projid, err := getProjectID(path)
if err != nil {
return projid, err
}
if projid > 0 {
q.quotas[path] = projid
}
if state.nextProjectID <= projid {
state.nextProjectID = projid + 1
}
return projid, nil
}
files, err := ioutil.ReadDir(home) files, err := ioutil.ReadDir(home)
if err != nil { if err != nil {
return errors.Errorf("read directory failed: %s", home) return errors.Errorf("read directory failed: %s", home)
@ -291,15 +345,26 @@ func (q *Control) findNextProjectID(home string) error {
continue continue
} }
path := filepath.Join(home, file.Name()) path := filepath.Join(home, file.Name())
projid, err := getProjectID(path) projid, err := checkProjID(path)
if err != nil { if err != nil {
return err return err
} }
if projid > 0 { if projid > 0 && projid != baseID {
q.quotas[path] = projid continue
} }
if q.nextProjectID <= projid { subfiles, err := ioutil.ReadDir(path)
q.nextProjectID = projid + 1 if err != nil {
return errors.Errorf("read directory failed: %s", path)
}
for _, subfile := range subfiles {
if !subfile.IsDir() {
continue
}
subpath := filepath.Join(path, subfile.Name())
_, err := checkProjID(subpath)
if err != nil {
return err
}
} }
} }

View file

@ -14,6 +14,5 @@ type Quota struct {
type Control struct { type Control struct {
backingFsBlockDev string backingFsBlockDev string
sync.RWMutex // protect nextProjectID and quotas map sync.RWMutex // protect nextProjectID and quotas map
nextProjectID uint32
quotas map[string]uint32 quotas map[string]uint32
} }