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:
parent
1a5b7f50bc
commit
31ed121cb8
2 changed files with 82 additions and 18 deletions
|
@ -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
|
||||||
|
}
|
||||||
|
subfiles, err := ioutil.ReadDir(path)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
if q.nextProjectID <= projid {
|
|
||||||
q.nextProjectID = projid + 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue