Skip to content

Commit 5430363

Browse files
committed
feature: add diskquota support regular expression
Add diskquota support regular expression. Signed-off-by: Rudy Zhang <[email protected]>
1 parent 01a90a6 commit 5430363

File tree

10 files changed

+165
-15
lines changed

10 files changed

+165
-15
lines changed

apis/swagger.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,9 @@ definitions:
17561756
type: "object"
17571757
additionalProperties:
17581758
type: "string"
1759+
QuotaID:
1760+
type: "string"
1761+
description: "set disk quota by specified quota id"
17591762

17601763
ContainerCreateResp:
17611764
description: "response returned by daemon when container create successfully"

apis/types/container_config.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

daemon/mgr/container.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"os/exec"
88
"path"
99
"path/filepath"
10+
"regexp"
11+
"strconv"
1012
"strings"
1113
"time"
1214

@@ -20,6 +22,7 @@ import (
2022
"github.com/alibaba/pouch/pkg/collect"
2123
"github.com/alibaba/pouch/pkg/errtypes"
2224
"github.com/alibaba/pouch/pkg/meta"
25+
"github.com/alibaba/pouch/pkg/quota"
2326
"github.com/alibaba/pouch/pkg/randomid"
2427
"github.com/alibaba/pouch/pkg/utils"
2528

@@ -392,6 +395,11 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
392395
return nil, errors.Wrap(err, "failed to parse volume argument")
393396
}
394397

398+
// set mount point disk quota
399+
if err := mgr.setMountPointDiskQuota(ctx, meta); err != nil {
400+
return nil, errors.Wrap(err, "failed to set mount point disk quota")
401+
}
402+
395403
// set container basefs
396404
mgr.setBaseFS(ctx, meta, id)
397405

@@ -1424,6 +1432,60 @@ func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta
14241432
return nil
14251433
}
14261434

1435+
func (mgr *ContainerManager) setMountPointDiskQuota(ctx context.Context, c *ContainerMeta) error {
1436+
qid := 0
1437+
if c.Config.QuotaID != "" {
1438+
var err error
1439+
qid, err = strconv.Atoi(c.Config.QuotaID)
1440+
if err != nil {
1441+
return errors.Wrapf(err, "invalid argument, QuotaID: %s", c.Config.QuotaID)
1442+
}
1443+
}
1444+
1445+
// parse diskquota regexe
1446+
quotas := c.Config.DiskQuota
1447+
res := make([]*quota.RegExp, 0)
1448+
for path, size := range quotas {
1449+
re := regexp.MustCompile(path)
1450+
res = append(res, &quota.RegExp{re, path, size})
1451+
}
1452+
1453+
for _, mp := range c.Mounts {
1454+
// skip volume mount or replace mode mount
1455+
if mp.Name != "" || mp.Replace != "" || mp.Source == "" || mp.Destination == "" {
1456+
continue
1457+
}
1458+
1459+
// skip non-directory path.
1460+
if fd, err := os.Stat(mp.Source); err != nil || !fd.IsDir() {
1461+
continue
1462+
}
1463+
1464+
matched := false
1465+
for _, re := range res {
1466+
findStr := re.pattern.FindString(mp.Destination)
1467+
if findStr == mp.Destination {
1468+
quotas[mp.Destination] = re.size
1469+
matched = true
1470+
if re.path != ".*" {
1471+
break
1472+
}
1473+
}
1474+
}
1475+
1476+
if matched {
1477+
err := quota.SetDiskQuota(mp.Source, quotas[mp.Destination], uint32(qid))
1478+
if err != nil {
1479+
return err
1480+
}
1481+
}
1482+
}
1483+
1484+
c.Config.DiskQuota = quotas
1485+
1486+
return nil
1487+
}
1488+
14271489
func (mgr *ContainerManager) detachVolumes(ctx context.Context, c *ContainerMeta) error {
14281490
for name := range c.Config.Volumes {
14291491
v, err := mgr.VolumeMgr.Get(ctx, name)

daemon/mgr/spec_blkio.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ func setupDiskQuota(ctx context.Context, meta *ContainerMeta, spec *SpecWrapper)
9797

9898
rootFSQuota, ok := meta.Config.DiskQuota["/"]
9999
if !ok || rootFSQuota == "" {
100-
return nil
100+
commonQuota, ok := meta.Config.DiskQuota[".*"]
101+
if !ok || commonQuota == "" {
102+
return nil
103+
}
104+
rootFSQuota = commonQuota
101105
}
102106

103107
if s.Hooks == nil {
@@ -114,7 +118,7 @@ func setupDiskQuota(ctx context.Context, meta *ContainerMeta, spec *SpecWrapper)
114118

115119
quotaPrestart := specs.Hook{
116120
Path: target,
117-
Args: []string{"set-diskquota", meta.BaseFS, rootFSQuota},
121+
Args: []string{"set-diskquota", meta.BaseFS, rootFSQuota, meta.Config.QuotaID},
118122
}
119123
s.Hooks.Prestart = append(s.Hooks.Prestart, quotaPrestart)
120124

pkg/quota/grpquota.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func (quota *GrpQuota) SetSubtree(dir string, qid uint32) (uint32, error) {
130130
}
131131

132132
// SetDiskQuota is used to set quota for directory.
133-
func (quota *GrpQuota) SetDiskQuota(dir string, size string, quotaID int) error {
133+
func (quota *GrpQuota) SetDiskQuota(dir string, size string, quotaID uint32) error {
134134
logrus.Debugf("set disk quota, dir: %s, size: %s, quotaID: %d", dir, size, quotaID)
135135
if !UseQuota {
136136
return nil
@@ -144,8 +144,8 @@ func (quota *GrpQuota) SetDiskQuota(dir string, size string, quotaID int) error
144144
return fmt.Errorf("mountpoint not found: %s", dir)
145145
}
146146

147-
id, err := quota.SetSubtree(dir, uint32(quotaID))
148-
if id == 0 {
147+
id, err := quota.SetSubtree(dir, quotaID)
148+
if err != nil || id == 0 {
149149
return fmt.Errorf("subtree not found: %s %v", dir, err)
150150
}
151151

pkg/quota/prjquota.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (quota *PrjQuota) SetSubtree(dir string, qid uint32) (uint32, error) {
100100
}
101101

102102
// SetDiskQuota is used to set quota for directory.
103-
func (quota *PrjQuota) SetDiskQuota(dir string, size string, quotaID int) error {
103+
func (quota *PrjQuota) SetDiskQuota(dir string, size string, quotaID uint32) error {
104104
logrus.Debugf("set disk quota, dir: %s, size: %s, quotaID: %d", dir, size, quotaID)
105105
if !UseQuota {
106106
return nil
@@ -114,8 +114,8 @@ func (quota *PrjQuota) SetDiskQuota(dir string, size string, quotaID int) error
114114
return fmt.Errorf("mountpoint not found: %s", dir)
115115
}
116116

117-
id, err := quota.SetSubtree(dir, uint32(quotaID))
118-
if id == 0 {
117+
id, err := quota.SetSubtree(dir, quotaID)
118+
if err != nil || id == 0 {
119119
return fmt.Errorf("subtree not found: %s %v", dir, err)
120120
}
121121

pkg/quota/quota.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var (
2424
type BaseQuota interface {
2525
StartQuotaDriver(dir string) (string, error)
2626
SetSubtree(dir string, qid uint32) (uint32, error)
27-
SetDiskQuota(dir string, size string, quotaID int) error
27+
SetDiskQuota(dir string, size string, quotaID uint32) error
2828
CheckMountpoint(devID uint64) (string, bool, string)
2929
GetFileAttr(dir string) uint32
3030
SetFileAttr(dir string, id uint32) error
@@ -92,7 +92,7 @@ func SetSubtree(dir string, qid uint32) (uint32, error) {
9292
}
9393

9494
// SetDiskQuota is used to set quota for directory.
95-
func SetDiskQuota(dir string, size string, quotaID int) error {
95+
func SetDiskQuota(dir string, size string, quotaID uint32) error {
9696
return Gquota.SetDiskQuota(dir, size, quotaID)
9797
}
9898

pkg/quota/set_diskquota.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io"
77
"os"
88
"path/filepath"
9+
"strconv"
910
"strings"
1011

1112
"github.com/docker/docker/pkg/reexec"
@@ -37,13 +38,15 @@ func processSetQuotaReexec() {
3738
}
3839
}()
3940

40-
if len(os.Args) != 3 {
41-
err = fmt.Errorf("invalid arguments: %v, it should be: %s: <path> <size>", os.Args, os.Args[0])
41+
if len(os.Args) != 4 {
42+
err = fmt.Errorf("invalid arguments: %v, it should be: %s: <path> <size> <quota id>", os.Args, os.Args[0])
4243
return
4344
}
4445

4546
basefs := os.Args[1]
4647
size := os.Args[2]
48+
id, _ := strconv.Atoi(os.Args[3])
49+
qid = uint32(id)
4750

4851
logrus.Infof("set diskquota: %v", os.Args)
4952

@@ -60,18 +63,18 @@ func processSetQuotaReexec() {
6063
return
6164
}
6265

63-
qid, err = SetSubtree(dir, qid)
66+
qid, err = SetSubtree(dir, uint32(qid))
6467
if err != nil {
6568
logrus.Errorf("failed to set subtree: %v", err)
6669
return
6770
}
6871

69-
err = SetDiskQuota(dir, size, int(qid))
72+
err = SetDiskQuota(dir, size, qid)
7073
if err != nil {
7174
logrus.Errorf("failed to set disk quota: %v", err)
7275
}
7376

74-
setQuotaForDir(dir, qid)
77+
setQuotaForDir(dir, uint32(qid))
7578
}
7679

7780
return

pkg/quota/type.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package quota
2+
3+
import "regexp"
4+
5+
// RegExp defines the regular expression of disk quota.
6+
type RegExp struct {
7+
pattern *regexp.Regexp
8+
path string
9+
size string
10+
}

test/cli_run_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ func (suite *PouchRunSuite) TestRunWithDiskQuota(c *check.C) {
785785
for _, line := range strings.Split(out, "\n") {
786786
if strings.Contains(line, "/") && strings.Contains(line, "2048000") {
787787
found = true
788+
break
788789
}
789790
}
790791

@@ -812,3 +813,65 @@ func (suite *PouchRunSuite) TestRunWithAnnotation(c *check.C) {
812813
c.Assert(util.PartialEqual(annotationStr, "a=b"), check.IsNil)
813814
c.Assert(util.PartialEqual(annotationStr, "foo=bar"), check.IsNil)
814815
}
816+
817+
// TestRunWithDiskQuotaRegular tests running container with --disk-quota.
818+
func (suite *PouchRunSuite) TestRunWithDiskQuotaRegular(c *check.C) {
819+
if !environment.IsDiskQuota() {
820+
c.Skip("Host does not support disk quota")
821+
}
822+
823+
volumeName := "diskquota-volume"
824+
containerName := "diskquota-regular"
825+
826+
ret := command.PouchRun("volume", "create", "-n", volumeName, "-o", "size=256m", "-o", "mount=/data/volume")
827+
defer func() {
828+
command.PouchRun("volume", "rm", volumeName).Assert(c, icmd.Success)
829+
}()
830+
ret.Assert(c, icmd.Success)
831+
832+
ret = command.PouchRun("run",
833+
"--disk-quota=1024m",
834+
`--disk-quota=".*=512m"`,
835+
`--disk-quota="/mnt/mount1=768m"`,
836+
"-v", "/data/mount1:/mnt/mount1",
837+
"-v", "/data/mount2:/mnt/mount2",
838+
"-v", "diskquota-volume:/mnt/mount3",
839+
"--name", containerName, busyboxImage, "df")
840+
defer func() {
841+
command.PouchRun("rm", "-f", containerName).Assert(c, icmd.Success)
842+
}()
843+
ret.Assert(c, icmd.Success)
844+
845+
out := ret.Stdout()
846+
847+
rootFound := false
848+
mount1Found := false
849+
mount2Found := false
850+
mount3Found := false
851+
for _, line := range strings.Split(out, "\n") {
852+
if strings.Contains(line, "/") && strings.Contains(line, "1048576") {
853+
rootFound = true
854+
continue
855+
}
856+
857+
if strings.Contains(line, "/mnt/mount1") && strings.Contains(line, "786432") {
858+
mount1Found = true
859+
continue
860+
}
861+
862+
if strings.Contains(line, "/mnt/mount2") && strings.Contains(line, "524288") {
863+
mount2Found = true
864+
continue
865+
}
866+
867+
if strings.Contains(line, "/mnt/mount3") && strings.Contains(line, "262144") {
868+
mount3Found = true
869+
continue
870+
}
871+
}
872+
873+
c.Assert(rootFound, check.Equals, true)
874+
c.Assert(mount1Found, check.Equals, true)
875+
c.Assert(mount2Found, check.Equals, true)
876+
c.Assert(mount3Found, check.Equals, true)
877+
}

0 commit comments

Comments
 (0)