Skip to content

Commit

Permalink
feature: update restful api support update container diskquota
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Wan <zirenwan@gmail.com>
  • Loading branch information
HusterWan committed Apr 27, 2018
1 parent 98ec8f3 commit 32d3401
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 91 deletions.
3 changes: 3 additions & 0 deletions apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2115,6 +2115,9 @@ definitions:
type: "array"
items:
type: "string"
DiskQuota:
type: "string"
description: "update disk quota for container"

ContainerUpgradeConfig:
description: |
Expand Down
11 changes: 11 additions & 0 deletions apis/types/update_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cli/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type UpdateCommand struct {
baseCommand
container
image string
quota string
}

// Init initialize update command.
Expand Down Expand Up @@ -51,6 +52,7 @@ func (uc *UpdateCommand) addFlags() {
flagSet.StringSliceVarP(&uc.env, "env", "e", nil, "Set environment variables for container")
flagSet.StringSliceVarP(&uc.labels, "label", "l", nil, "Set label for container")
flagSet.StringVar(&uc.restartPolicy, "restart", "", "Restart policy to apply when container exits")
flagSet.StringVar(&uc.quota, "quota", "", "Update disk quota for container")
}

// updateRun is the entry of update command.
Expand Down Expand Up @@ -99,6 +101,7 @@ func (uc *UpdateCommand) updateRun(args []string) error {
Label: uc.labels,
RestartPolicy: restartPolicy,
Resources: resource,
DiskQuota: uc.quota,
}

apiClient := uc.cli.Client()
Expand Down
64 changes: 64 additions & 0 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,11 @@ func (mgr *ContainerManager) Update(ctx context.Context, name string, config *ty
}
}

// update container disk quota
if err := mgr.updateContainerDiskQuota(ctx, c.meta, config.DiskQuota); err != nil {
return fmt.Errorf("failed to update container %s diskquota: %v", c.ID(), err)
}

// update Resources of a container.
if err := mgr.updateContainerResources(c.meta, config.Resources); err != nil {
return fmt.Errorf("failed to update container %s resources: %v", c.ID(), err)
Expand Down Expand Up @@ -948,6 +953,65 @@ func (mgr *ContainerManager) Update(ctx context.Context, name string, config *ty
return updateErr
}

func (mgr *ContainerManager) updateContainerDiskQuota(ctx context.Context, c *ContainerMeta, diskQuota string) error {
if diskQuota == "" {
return nil
}

quotaMap, err := opts.ParseDiskQuota([]string{diskQuota})
if err != nil {
return errors.Wrapf(err, "failed to parse disk quota")
}

c.Config.DiskQuota = quotaMap

// set mount point disk quota
if err := mgr.setMountPointDiskQuota(ctx, c); err != nil {
return errors.Wrapf(err, "failed to set mount point disk quota")
}

var qid uint32
if c.Config.QuotaID != "" {
id, err := strconv.Atoi(c.Config.QuotaID)
if err != nil {
return errors.Wrapf(err, "invalid argument, QuotaID: %s", c.Config.QuotaID)
}

// if QuotaID is < 0, it means pouchd alloc a unique quota id.
if id < 0 {
qid, err = quota.GetNextQuatoID()
if err != nil {
return errors.Wrap(err, "failed to get next quota id")
}

// update QuotaID
c.Config.QuotaID = strconv.Itoa(int(qid))
} else {
qid = uint32(id)
}
}

// get rootfs quota
defaultQuota := quota.GetDefaultQuota(quotaMap)
if qid > 0 && defaultQuota == "" {
return fmt.Errorf("set quota id but have no set default quota size")
}
// update container rootfs disk quota
status := c.State.Status
if (status == types.StatusRunning || status == types.StatusPaused) && c.Snapshotter != nil {
basefs, ok := c.Snapshotter.Data["MergedDir"]
if !ok || basefs == "" {
return fmt.Errorf("Container is running, but MergedDir is missing")
}

if err := quota.SetRootfsDiskQuota(basefs, defaultQuota, qid); err != nil {
return errors.Wrapf(err, "failed to set container rootfs diskquota")
}
}

return nil
}

// updateContainerResources update container's resources parameters.
func (mgr *ContainerManager) updateContainerResources(c *ContainerMeta, resources types.Resources) error {
// update resources of container.
Expand Down
97 changes: 97 additions & 0 deletions storage/quota/quota.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package quota

import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"syscall"

"github.com/alibaba/pouch/pkg/kernel"
Expand Down Expand Up @@ -139,3 +145,94 @@ func GetDefaultQuota(quotas map[string]string) string {

return ""
}

// SetRootfsDiskQuota is to set container rootfs dir disk quota.
func SetRootfsDiskQuota(basefs, size string, quotaID uint32) error {
overlayfs, err := getOverlay(basefs)
if err != nil || overlayfs == nil {
return fmt.Errorf("failed to get lowerdir: %v", err)
}

for _, dir := range []string{overlayfs.Upper, overlayfs.Work} {
_, err = StartQuotaDriver(dir)
if err != nil {
return fmt.Errorf("failed to start quota driver: %v", err)
}

quotaID, err = SetSubtree(dir, quotaID)
if err != nil {
return fmt.Errorf("failed to set subtree: %v", err)
}

err = SetDiskQuota(dir, size, quotaID)
if err != nil {
return fmt.Errorf("failed to set disk quota: %v", err)
}

return setQuotaForDir(dir, quotaID)
}

return nil
}

func setQuotaForDir(src string, qid uint32) error {
filepath.Walk(src, func(path string, fd os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("setQuota walk dir %s get error %v", path, err)
}

SetFileAttrNoOutput(path, qid)
return nil
})

return nil
}

func getOverlay(basefs string) (*OverlayMount, error) {
overlayfs := &OverlayMount{}

fd, err := os.Open("/proc/mounts")
if err != nil {
return nil, err
}
defer fd.Close()

br := bufio.NewReader(fd)
for {
line, _, c := br.ReadLine()
if c == io.EOF {
break
}

parts := strings.Split(string(line), " ")
if len(parts) != 6 {
continue
}
if parts[1] != basefs || parts[2] != "overlay" {
continue
}

mountParams := strings.Split(parts[3], ",")
for _, p := range mountParams {
switch {
case strings.Contains(p, "lowerdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Lower = s[1]
}

case strings.Contains(p, "upperdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Upper = s[1]
}

case strings.Contains(p, "workdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Work = s[1]
break
}
}
}
}

return overlayfs, nil
}
92 changes: 1 addition & 91 deletions storage/quota/set_diskquota.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package quota

import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/docker/docker/pkg/reexec"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -53,93 +49,7 @@ func processSetQuotaReexec() {

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

overlayfs, err := getOverlay(basefs)
if err != nil || overlayfs == nil {
logrus.Errorf("failed to get lowerdir: %v", err)
return
}

for _, dir := range []string{overlayfs.Upper, overlayfs.Work} {
_, err = StartQuotaDriver(dir)
if err != nil {
logrus.Errorf("failed to start quota driver: %v", err)
return
}

qid, err = SetSubtree(dir, uint32(qid))
if err != nil {
logrus.Errorf("failed to set subtree: %v", err)
return
}

err = SetDiskQuota(dir, size, qid)
if err != nil {
logrus.Errorf("failed to set disk quota: %v", err)
return
}

setQuotaForDir(dir, uint32(qid))
}
err = SetRootfsDiskQuota(basefs, size, qid)

return
}

func setQuotaForDir(src string, qid uint32) {
filepath.Walk(src, func(path string, fd os.FileInfo, err error) error {
if err != nil {
logrus.Warnf("setQuota walk dir %s get error %v", path, err)
return nil
}
SetFileAttrNoOutput(path, qid)
return nil
})
}

func getOverlay(basefs string) (*OverlayMount, error) {
overlayfs := &OverlayMount{}

fd, err := os.Open("/proc/mounts")
if err != nil {
return nil, err
}
defer fd.Close()

br := bufio.NewReader(fd)
for {
line, _, c := br.ReadLine()
if c == io.EOF {
break
}

parts := strings.Split(string(line), " ")
if len(parts) != 6 {
continue
}
if parts[1] != basefs || parts[2] != "overlay" {
continue
}

mountParams := strings.Split(parts[3], ",")
for _, p := range mountParams {
switch {
case strings.Contains(p, "lowerdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Lower = s[1]
}

case strings.Contains(p, "upperdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Upper = s[1]
}

case strings.Contains(p, "workdir"):
if s := strings.Split(p, "="); len(s) == 2 {
overlayfs.Work = s[1]
break
}
}
}
}

return overlayfs, nil
}

0 comments on commit 32d3401

Please sign in to comment.