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

added support for tmpfs-mode in compose file #808

Merged
merged 3 commits into from
Jan 16, 2018

Conversation

ethan-haynes
Copy link

@ethan-haynes ethan-haynes commented Jan 14, 2018

Provides a fix for #698

- What I did
Added the ability to use a tmpfs volume type in compose with the optional parameter of size.
- How I did it
Changed compose schemas 3.2-3.5 to allow for the type. Tweaked volumes parser to read it.
Removed it from unapproved params.
- How to verify it

Test it with compose:

version: "3.6"
services:
  tmpfs:
    image: nginx:latest
    volumes:
      - type: tmpfs
        target: /app
        tmpfs:
          size: 10000
$ docker stack deploy -c swarm-compose.yml nginx

@codecov-io
Copy link

codecov-io commented Jan 14, 2018

Codecov Report

Merging #808 into master will increase coverage by 0.08%.
The diff coverage is 100%.

@@            Coverage Diff             @@
##           master     #808      +/-   ##
==========================================
+ Coverage   50.92%   51.01%   +0.08%     
==========================================
  Files         237      237              
  Lines       15332    15362      +30     
==========================================
+ Hits         7808     7837      +29     
- Misses       7024     7025       +1     
  Partials      500      500

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

Design LGTM

@thaJeztah
Copy link
Member

design looks good to me as well; moving to code review

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

Thanks! Left some comments

@@ -261,6 +261,12 @@
"properties": {
"nocopy": {"type": "boolean"}
}
},
"tmpfs": {
Copy link
Member

Choose a reason for hiding this comment

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

The 3.2 ... 3.5 formats have been released, so those formats are now frozen; to add a new feature to the existing formats, a new version (3.6) needs to be created.

@@ -15,7 +15,6 @@ const endOfSpec = rune(0)
// ParseVolume parses a volume spec without any knowledge of the target platform
func ParseVolume(spec string) (types.ServiceVolumeConfig, error) {
volume := types.ServiceVolumeConfig{}

Copy link
Member

Choose a reason for hiding this comment

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

can you revert this change?

}

// Anonymous volumes
if volume.Source == "" && volume.Type != "tmpfs" {
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps this was already in the existing code, but this looks like a bug, as this allows a type=bind without specifying a source;

version: "3.5"
services:
  tmpfs:
    image: nginx:alpine
    volumes:
      - type: bind
        target: /app

I suggest moving this validation into the handleVolumeToMount(), handleBindToMount(), and handleTmpfsToMount() functions; it makes the logic easier to follow, and puts validation with each type.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, interesting; it looks like there's actually a different issue as well; if nosource: is specified, docker stack deploy resolves this to the directory of the docker-compose.yml; I think that's not the correct behaviour; if no source is specified, it should produce an error (if you want to use the directory of the docker-compose file, an explicit source: "./" or source: "." should be required)

@dnephin @vdemeester ^^ wdyt?

}
if volume.Tmpfs != nil {
result.TmpfsOptions = &mount.TmpfsOptions{
SizeBytes: volume.Tmpfs.Size,
Copy link
Member

Choose a reason for hiding this comment

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

Do we need validation of the Size property itself? (i.e., should likely be a positive integer)

Possibly this can be handled by the JSON-schema (see my other comment), but we should check if that is sufficient

volume composetypes.ServiceVolumeConfig,
result mount.Mount,
) (mount.Mount, error) {
// source must be empty for tmpfs
Copy link
Member

Choose a reason for hiding this comment

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

This comment is a bit redundant

stackVolumes volumes,
namespace Namespace,
) (mount.Mount, error) {
result := mount.Mount{
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a big fan of moving this outside of the handleXX functions; I assume you kept it out to reduce code duplication, but now the conversion (and validation) is "split" between the handleXX functions and this function.

Given that not all mount-types use the same options in this type (e.g., type=bind uses Source, but type=tmpfs doesn't), I'd move this code to each handleXX function. If duplication of code is a concern (but I think it's minimal), you could have a helper function for the shared code

Propagation: mount.Propagation(volume.Bind.Propagation),
}
}
// Binds volumes
Copy link
Member

Choose a reason for hiding this comment

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

This comment doesn't add much, so I'd remove it

SizeBytes: volume.Tmpfs.Size,
}
}
// Tmpfs volumes
Copy link
Member

Choose a reason for hiding this comment

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

This comment doesn't add much, so I'd remove it; if the function needs documentation, add a GoDoc for the function itself

"tmpfs": {
"type": "object",
"properties": {
"size": {"type": "number"}
Copy link
Member

Choose a reason for hiding this comment

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

should this be "type": "integer" ?

If only positive integers are allowed, likely:

{
  "type": "integer",
  "exclusiveMinimum": 0
}

(not sure if exclusiveMinimum or minimum; likely depends on how empty values are handled)

Copy link
Author

@ethan-haynes ethan-haynes Jan 15, 2018

Choose a reason for hiding this comment

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

In the Docker documentation if tmpfs-size isn't provided it will be unlimited by default:

Option Description
tmpfs-size Size of the tmpfs mount in bytes. Unlimited by default.

So if the user wants to specify a ceiling of how many bytes to allocate, then they should provide a positive integer above zero. The docker cli, as it behaves now, will not allow negative numbers:

invalid argument "type=tmpfs,destination=/app,tmpfs-size=-1" for --mount: invalid value for tmpfs-size: -1

It will allow zero, but when you inspect the container it just defaults TmpfsOptions to an empty object, which (since tmpfs-size won't actually be set) will allocate "unlimited" resources:

"Mounts": [
                        {
                            "Type": "tmpfs",
                            "Target": "/app",
                            "TmpfsOptions": {}
                        }
                    ]

So I think we should, as you said, use an exclusiveMinimum of zero. That way the user at least attaches the TmpfsOptions with a value above zero for tmpfs-size. I will go ahead and add that to the 3.6v schema, unless someone thinks it should be handled differently.

Copy link
Member

Choose a reason for hiding this comment

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

If the CLI uses zero as equivalent of "not set", then probably minimum should be used (zero is a valid value, but indicates "no limit").

Copy link
Author

Choose a reason for hiding this comment

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

That makes sense. I will Git started on those changes :octocat:

@@ -296,6 +296,11 @@ type ServiceVolumeVolume struct {
NoCopy bool `mapstructure:"nocopy"`
}

// ServiceVolumeTmpfs are options for a service volume of type tmpfs
type ServiceVolumeTmpfs struct {
Size int64
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be an uint64? Or is a signed integer used elsewhere?

Copy link
Author

Choose a reason for hiding this comment

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

Chose to use int64 over unit64 because the mount struct looked to expect TmpfsOptions to contain it. Source:
https://github.com/moby/moby/blob/6415f1dcf5c79da853c604733c56b43ae3d18ced/api/types/mount/mount.go#L108

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Wonder if that was for a reason ("special -1 value, e.g.) or just overlooked during review of the initial implementation. 🤔

@ethan-haynes
Copy link
Author

Thanks for the helpful feedback. I will make the necessary changes and update the PR.

@ethan-haynes
Copy link
Author

I believe that I addressed most of the concerns that @thaJeztah brought up in my latest PR, with the exception being the issue regarding the directory of the compose file being set when a source is not passed to a volume of type bind (using the long compose syntax). I added explicit checks for an empty source in the convert package, but by the time it is received there it is already set. I am happy to alter/fix the behavior in this PR if it is decided that the current functionality is not desired. Or we/I can make a separate issue for it and address it in a different PR. Thanks! 😄

@dnephin
Copy link
Contributor

dnephin commented Jan 15, 2018

Not sure why jenkins didn't run the integration suite.

I forced a build from https://jenkins.dockerproject.org/job/docker/job/cli/view/change-requests/

Copy link
Contributor

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

Thanks, this is looking good. Just a couple minor comments.

) (mount.Mount, error) {

switch volume.Type {
case "volume":
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the old default (when type is not set) would be volume, so I guess this case should also have ""

if volume.Type == "bind" && volume.Volume != nil {
return result, errors.New("volume options are incompatible with type bind")
if volume.Type != "tmpfs" {
result.Source = volume.Source
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't really necessary because handleTmpfsToMount() will error out in this case. I would remove this so we don't have to check it in two places.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

Thanks @ethan-haynes! Changes in this PR look good to me 🎉

I have one nit left;

Can you squash your commits (we tend to keep changes made following "review comments" out of our git history), but keep separate commits for the compose-file spec changes, so:

  1. 1 commit that copies the compose 3.5 spec to 3.6 (and updates the cli/compose/schema/bindata.go accordingly)
  2. 1 commit that adds the new "tmpfs" field to the compose file spec (and updates the cli/compose/schema/bindata.go accordingly)
  3. 1 commit with your code changes

(You can combine 2. and 3. in a single commit if you prefer)

Doing so makes it easier to see the changes that were made in the new 3.6 spec, and makes it easier to find which commit added the new field in git history.

Ethan Haynes added 3 commits January 16, 2018 10:44
Signed-off-by: Ethan Haynes <ethanhaynes@alumni.harvard.edu>
Signed-off-by: Ethan Haynes <ethanhaynes@alumni.harvard.edu>
Signed-off-by: Ethan Haynes <ethanhaynes@alumni.harvard.edu>
Copy link
Contributor

@dnephin dnephin left a comment

Choose a reason for hiding this comment

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

LGTM with squashed commits

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

perfect! LGTM, thanks so much 👍

@thaJeztah
Copy link
Member

ping @shin- for Docker Compose

@thaJeztah
Copy link
Member

Oh! before I forget; @ethan-haynes could you also take care of updating the compose-file documentation? Possibly needs to go in the vnext-compose branch; https://github.com/docker/docker.github.io/tree/vnext-compose/compose but @mistyhacks may be able to give guidance there 😊

@mdlinville
Copy link
Contributor

Yes, that's correct if it applies to a not-yet-released feature of Compose.

@ethan-haynes
Copy link
Author

Sounds good. I'll go ahead and update the documentation with new examples for the 3.6v compose release. 📝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants