Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add local disk quota #725

Merged
merged 1 commit into from
Feb 23, 2018

Conversation

rudyfly
Copy link
Collaborator

@rudyfly rudyfly commented Feb 11, 2018

Ⅰ. Describe what this PR did

Add local disk quota. In alikernel, with dirquota feature to limit
directory block size.

Ⅱ. Does this pull request fix one issue?

fixes #101

Ⅲ. Describe how you did it

Ⅳ. Describe how to verify it

In alikernel, you can use it like this:
set volume size is 10G

#pouch volume create -d local -n pouchvolume -o size=10g
CreatedAt:
Driver:       local
Mountpoint:
Name:         pouchvolume
Scope:

run a container, use df to see the volume size.

# pouch run -ti -v pouchvolume:/mnt --name test registry.hub.docker.com/library/busybox:latest df -h
Filesystem                Size      Used Available Use% Mounted on
overlay                  29.4G     19.6G      8.3G  70% /
tmpfs                    64.0M         0     64.0M   0% /dev
shm                      64.0M         0     64.0M   0% /dev/shm
tmpfs                    64.0M         0     64.0M   0% /run
/dev/sda3                10.0G      4.0K     10.0G   0% /mnt

Ⅴ. Special notes for reviews

Signed-off-by: Rudy Zhang rudyflyzhang@gmail.com

return st.Dev, nil
}

func toByteSize(s string) uint64 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a unit test for this function? @rudyfly 😄

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this is similar to the function ToBytes in vendor package code.cloudfouncry.org/bytefmt/bytes.go.
We have a plan to remove this vendored package, could you encapsulate them into a package in pkg.
See #356


// BaseQuota defines the quota operation interface.
type BaseQuota interface {
QuotaDriverStart(dir string) (string, error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my own point of view, StartQuotaDriver is better than QuotaDriverStart. WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@rudyfly rudyfly force-pushed the volume branch 2 times, most recently from 36a4fcf to 2390e5e Compare February 11, 2018 08:31
@codecov-io
Copy link

codecov-io commented Feb 11, 2018

Codecov Report

Merging #725 into master will decrease coverage by <.01%.
The diff coverage is 0%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #725      +/-   ##
==========================================
- Coverage   13.58%   13.57%   -0.01%     
==========================================
  Files          97       97              
  Lines        5927     5928       +1     
==========================================
  Hits          805      805              
- Misses       5068     5069       +1     
  Partials       54       54
Impacted Files Coverage Δ
daemon/mgr/container.go 3.18% <ø> (ø) ⬆️
daemon/mgr/volume.go 0% <ø> (ø) ⬆️
cli/cli.go 0% <0%> (ø) ⬆️
pkg/exec/command.go 0% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c18ff53...d964f2e. Read the comment docs.

continue
}
parts := strings.Split(line, " ")
if len(parts) == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think

if len(parts) == 0 {
    continue
}

is redundant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it should be < 2


var (
lock sync.Mutex
quotaLastID uint32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add more description or comments on these global variables in pkg local?
I think this is very useful for others to understand your thoughts. Actually currently I can read your code with logic, but have no idea about your thoughts. 😢

return quota.setUserQuota(id, limit, mountPoint)
}

// CheckMountpoint is used to check mount point.
Copy link
Collaborator

@allencloud allencloud Feb 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add one example valid line in /proc/mounts in comment please?
like:

// CheckMountpoint is used to check mount point which is in file /proc/mounts.
// one valid line must have the following format:
// cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0

You can also tell others more about the exact meanings of each column. 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@allencloud
Copy link
Collaborator

allencloud commented Feb 14, 2018

That is a great work for you to complete this. @rudyfly
Actually I am new for project quota, so I am still learning from that. I just left some tiny comments that may need your help.
And I wish to finish my learning and code review in the late today. Thanks a lot. 😄 🍷


// on
exit, stdout, stderr, err := exec.Run(0, "quotaon", "-pg", mountPoint)
if err != nil && exit != 1 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be if err != nil || exit != 0?
Since if the exit code is non-zero, it means running into an error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exit = 1 is also valid.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that is some kind of strange. Could you add some illustration here to tell code readers about that?

@allencloud
Copy link
Collaborator

This is a great work. While I have one thing may need discussion.

Is there any possibility that we make volume quota drive as plugins.

Currently we have grpquota as the default quota driver, and to be honest, I am afraid we have not taken full advantage of the interface BaseQuota we defined. In the code, I found we use a global instance of gquota and function in pkg local func StartQuotaDriver(dir string) to transfer the request. We did not use gquota.StartQuotaDriver(dir) directly. And this way may weaken the usage of interface we defined and enhance the degree of code customization.

I am wondering if we could still

  • keep the interface BaseQuota
  • enhance the functionality of it to be capable of multiple specific quota driver plugins
  • make users have ability to input the quota driver options

Maybe we can define a global variable var QuotaDrivers map[string]BaseQuota, and in every interface BaseQuota implementation(such as grpquota), we add an init function init() to register grpquota instance into QuotaDrivers. Then we can use another function like

func GetQuotaDriver(dr string) (BaseQuota, error){
    if dr == "" {
        return grpquotaDriver,nil
    }
    driver, exist := QuotaDrivers(dr)
    if !exist{
        return fmt.Errorf("....")
    }
    return driver, nil
}

As a result, we could make users input their wanted quota drivers. And others could also add another quota driver via implementing the interface BaseQuota.

CreatedAt: volume.CreationTimestamp.Format("2006-1-2 15:04:05"),
Status: make(map[string]interface{}),
}
for k, v := range volume.Options() {
Copy link
Collaborator

@allencloud allencloud Feb 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move the following codes above just before respVolume's initialization. We can use

var status map[string]string
for k, v := range volume.Options() {
    if k != "" && v != "" {
        status[k] = v
    }
}

Then we can use this status in the respVolume initialization.

Although two ways can achieve the same effect, maybe we can make code more clear with two parts: data preparation and respVolume initialization. And this makes no separated assignments.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

},
Status: make(map[string]interface{}),
}
for k, v := range volume.Options() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as the previous comment.

@rudyfly rudyfly force-pushed the volume branch 4 times, most recently from 056c8b9 to 36f4f47 Compare February 23, 2018 01:46
Add local disk quota. In alikernel, with dirquota feature to limit
directory block size.

Signed-off-by: Rudy Zhang <rudyflyzhang@gmail.com>
@rudyfly
Copy link
Collaborator Author

rudyfly commented Feb 23, 2018

@allencloud PTAL 😄

@allencloud
Copy link
Collaborator

LGTM, thanks again for the work. How about the test cases and disk quota document? @rudyfly

@pouchrobot pouchrobot added the LGTM one maintainer or community participant agrees to merge the pull reuqest. label Feb 23, 2018
if devID == devID2 {
mountPoint = parts[1]
fsType = parts[2]
for _, opt := range strings.Split(parts[3], ",") {
Copy link
Collaborator

@allencloud allencloud Feb 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just use

if strings.Contains(parts[3], "prjquota") {
    hasQuota = true
}

?

if devID == devID2 {
mountPoint = parts[1]
fsType = parts[2]
for _, opt := range strings.Split(parts[3], ",") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just use

if strings.Contains(parts[3], "grpquota") {
    hasQuota = true
}

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature LGTM one maintainer or community participant agrees to merge the pull reuqest. size/XXL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[feature request] implement disk quota in pouch
4 participants