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

Change docker volume create to non-idempotent #27675

Conversation

yongtang
Copy link
Member

- What I did

This fix tries to address the issue raised in #16068 where non-idempotency is desired for docker volume create.

- How I did it

Previously docker volume create will silently ignore in case a volume with the same name has already been created with the same driver (idempotency). This fix made the change so that an already exist error is returned together with the already created volume v in Create/CreateWithRef func.

(So a check of return error type is needed if the caller wants idempotency instead)

- How to verify it

Tests has been added to cover the chagnes.

- Description for the changelog

An error will be returned for docker volume create in case a volume with the same name has been created by the same driver.

- A picture of a cute animal (not mandatory but encouraged)

This fix fixes #16068.

Signed-off-by: Yong Tang yong.tang.github@outlook.com

@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch 5 times, most recently from acfb44c to 2d2298b Compare October 24, 2016 22:06
@thaJeztah
Copy link
Member

Oh! I just noticed that this probably does the same as #27346 (haven't looked at the implementation yet)

Also we should take into account that we now store the options that were used when a volume was created, so perhaps volume create does not have to produce an error if all options are exactly the same

@allencloud
Copy link
Contributor

Oh, @thaJeztah
To be honest, maybe we should be more careful on what you suggest that same name with same options.

Here is my own thought.
Since if a user executes docker volume create --name aaa twice(same name same options), probably he indeed needs two different volumes.(maybe he forgot he created before or someone else did that) If dockerd make the second action succeed, afterwards the volume may be polluted. Its damage will be exposed.

Maybe needs more discussion in this part.

@@ -269,14 +273,16 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st
if v.DriverName() != driverName && driverName != "" && driverName != volume.DefaultDriverName {
return nil, errNameConflict
}
return v, nil
// In case a volume has already been created, we will return an error (non-idempotency)
return v, errAlreadyCreated
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 huge fan of this approach.

Ultimately the reason the code is as it is was to support the legacy auto-create/use for docker run -v <name>:/foo.
Meanwhile this pattern is racey:

Get(foo) || Create(foo)

Maybe we could have Get accept a callback that gets run at the end of the fn while the store is still locked.

@yongtang
Copy link
Member Author

yongtang commented Nov 2, 2016

@cpuguy83 @allencloud @thaJeztah I made some changes to the PR. Now instead of checking the error, the CreateWithRef will carry a arg of quiet bool. If quiet == true, then the behavior will be the same as before. If quiet == false, then an error already exist will be returned. Please take a look and let me know if this will fit the requirement.

return s.CreateWithRef(name, driverName, "", opts, labels)
// In case a volume with the name has already been created, an errAlreadyCreated error will be
// returned along with the already created volume, if `quiet` is not true
func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string, quiet bool) (volume.Volume, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is a good solution.
Bool parameters are never a good solution.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks @cpuguy83. No sure about the best way to handle the case. Any suggestions?

Copy link
Member

Choose a reason for hiding this comment

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

@yongtang I think it would be better to have a CreateIdempotent than a bool arg... or a set of functional arguments (http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)

@yongtang
Copy link
Member Author

yongtang commented Nov 7, 2016

Thanks @cpuguy83. The PR has been updated with functional options. Please take a look.

Copy link
Member

@cpuguy83 cpuguy83 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 definitely much cleaner.

Right now this is implemented more like a callback.
I wonder if it would be better to do more like a config struct:

ie, the functional argument signature would look like func(*CreateConfig) where in the argument you set options (like "Ref" and idempotency handling).

Then we always call for _, arg := range args { f(cfg) } at the beginning of Create and check the options struct in the function body as needed.

@yongtang
Copy link
Member Author

@cpuguy83 One issue I am encountering is that, idempotency check is done at the end of Get while setting Refs is done at the end of Create.

We may need to pass a flag to indicate if the volume is obtained from the existing one or created new, before invoking an array of option functionals.

I will spend more time thinking about if there is a better way to handle the scenario.

@cpuguy83
Copy link
Member

@yongtang Maybe we can just have an CreateIdempotent for docker run -v and a normal Create for docker volume create?

This will definitely need to be versioned in the API, though... and probably go through the deprecation policy.

@yongtang
Copy link
Member Author

yongtang commented Dec 1, 2016

Thanks @cpuguy83 for the suggestion. Let me take a look and update the PR accordingly.

@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from e409074 to c9f2680 Compare December 5, 2016 00:07
@yongtang
Copy link
Member Author

yongtang commented Dec 5, 2016

Thanks @cpuguy83. The PR has been updated to take API version into consideration. Also added the CreateIdempotent function. Please take a look.

@cpuguy83
Copy link
Member

cpuguy83 commented Dec 5, 2016

@yongtang Thanks! Seems like we're getting closer.
Now what if volumes.Create returned something like an ErrAlreadyExists when name and driver are the same. Then at the API we can determine how to respond to the client.

Namely I think in older API's we keep sending the same response (created), in newer API's we follow deprecation policy and send a 304 (not modified) for now, which the client can pick up and use to display a warning but otherwise not error out.
Then after the deprecation period is up we can return the error.

@cpuguy83
Copy link
Member

cpuguy83 commented Dec 5, 2016

Also, thanks for sticking with this!

@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from c9f2680 to 86ebd69 Compare December 6, 2016 23:05
@yongtang
Copy link
Member Author

yongtang commented Dec 7, 2016

Thanks @cpuguy83. The PR has been updated with 302 response. Please take a look.

func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts, labels map[string]string) (volume.Volume, error) {
// In case a volume has already been created with the same name and driver, the already
// created volumes will be returned
func (s *VolumeStore) CreateIdempotentWithRef(name, driverName, ref string, opts, labels map[string]string) (volume.Volume, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Just a note, we don't need this Idempotent change anymore

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks. idempotent has been removed.

@yongtang
Copy link
Member Author

yongtang commented Apr 7, 2017

Thanks @thaJeztah. I updated the PR. Now StatusNotModified will only write the header without the content:

                       w.WriteHeader(http.StatusNotModified)
                       return nil

The error message should be addressed. Please take a look.

func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
return s.CreateWithRef(name, driverName, "", opts, labels)
return s.CreateWithRef(name, driverName, "", opts, labels, func(vol volume.Volume) error {
Copy link
Member

Choose a reason for hiding this comment

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

This is the only place we're using this "found" and it seems legit to put this in the volume conflict lookup check.

@allencloud
Copy link
Contributor

Conflict happened, perhaps a rebase is needed. @yongtang

@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from efb5648 to c765c2e Compare June 20, 2017 02:18
@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from c765c2e to 0607ccf Compare June 22, 2017 23:42
@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from 0607ccf to 097e087 Compare June 23, 2017 02:55
@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from 0bd69af to 3a8c224 Compare June 24, 2017 18:39
@yongtang
Copy link
Member Author

The Jenkins failure are caused by recent changes in other places in Moby. Still looking into the issue.

@yongtang yongtang force-pushed the 16068-docker-volume-create-non-idempotency branch from 3a8c224 to 598adbd Compare June 28, 2017 01:07
This fix tries to address the issue raised in 16068 where
non-idempotency is desired for `docker volume create`.

Previously `docker volume create` will silently ignore in case
a volume with the same name has already been created with the
same driver (idempotency). This fix made the change so that
an `already exist` error is returned together with the already
created volume `v` in `Create/CreateWithRef` func.

The change is to add functional options to `Create` so that
caller has the option to return error if a volume has been
found.

Tests has been added to cover the chagnes.

This fix fixes 16068.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is an update of the last commit such that an error is only returned
if options are different for `docker volume create`.

The change is based on the feedback:
moby#27675 (comment)

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
@yongtang
Copy link
Member Author

@cpuguy83 The PR has been updated with "found" into volume conflict lookup check. Now all tests finally passes. (powerpc test is probably related to Jenkins server. will give it a try again later)

Please take a look and let me know if there are any issues.

@vieux
Copy link
Contributor

vieux commented Jul 4, 2017

ping @cpuguy83

@allencloud
Copy link
Contributor

conflict happens, and I am afraid @yongtang needs a bottle of beer and a rebase. 🍺

func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
return s.CreateWithRef(name, driverName, "", opts, labels)
return s.CreateWithRef(name, driverName, "", opts, labels, func(vol volume.Volume) error {
var options map[string]string
Copy link
Member

Choose a reason for hiding this comment

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

Why are we keeping this logic for CreateWithRef only?

Copy link
Member

Choose a reason for hiding this comment

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

ping @yongtang ^^

@vdemeester
Copy link
Member

ping @thaJeztah @yongtang what is the status here ?

@AkihiroSuda
Copy link
Member

ping @yongtang ^^

@cpuguy83
Copy link
Member

Closing as this has a number of conflicts at this point and hasn't been updated in a long time.
Also see #36688 which re-writes much of the volume handling code.

@cpuguy83 cpuguy83 closed this May 25, 2018
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.

Docker volume create should not be idempotent (by default)
10 participants