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
Change docker volume create
to non-idempotent
#27675
Conversation
acfb44c
to
2d2298b
Compare
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 |
Oh, @thaJeztah Here is my own thought. Maybe needs more discussion in this part. |
volume/store/store.go
Outdated
@@ -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 |
There was a problem hiding this comment.
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.
2d2298b
to
616b923
Compare
@cpuguy83 @allencloud @thaJeztah I made some changes to the PR. Now instead of checking the error, the |
volume/store/store.go
Outdated
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
616b923
to
e409074
Compare
Thanks @cpuguy83. The PR has been updated with functional options. Please take a look. |
There was a problem hiding this 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.
@cpuguy83 One issue I am encountering is that, idempotency check is done at the end of 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. |
@yongtang Maybe we can just have an This will definitely need to be versioned in the API, though... and probably go through the deprecation policy. |
Thanks @cpuguy83 for the suggestion. Let me take a look and update the PR accordingly. |
e409074
to
c9f2680
Compare
Thanks @cpuguy83. The PR has been updated to take API version into consideration. Also added the CreateIdempotent function. Please take a look. |
@yongtang Thanks! Seems like we're getting closer. 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. |
Also, thanks for sticking with this! |
c9f2680
to
86ebd69
Compare
Thanks @cpuguy83. The PR has been updated with 302 response. Please take a look. |
volume/store/store.go
Outdated
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
Thanks @thaJeztah. I updated the PR. Now
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 { |
There was a problem hiding this comment.
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.
Conflict happened, perhaps a rebase is needed. @yongtang |
efb5648
to
c765c2e
Compare
c765c2e
to
0607ccf
Compare
0607ccf
to
097e087
Compare
0bd69af
to
3a8c224
Compare
The Jenkins failure are caused by recent changes in other places in Moby. Still looking into the issue. |
3a8c224
to
598adbd
Compare
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>
598adbd
to
8c668b2
Compare
@cpuguy83 The PR has been updated with "found" into volume conflict lookup check. Now all tests finally passes. ( Please take a look and let me know if there are any issues. |
ping @cpuguy83 |
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @yongtang ^^
ping @thaJeztah @yongtang what is the status here ? |
ping @yongtang ^^ |
Closing as this has a number of conflicts at this point and hasn't been updated in a long time. |
- 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 analready exist
error is returned together with the already created volumev
inCreate/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