-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Adding "driver" and "dangling" filter options to "docker volume ls" #2831
Conversation
api/handlers.go
Outdated
@@ -359,6 +383,18 @@ func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
|
|||
found = false | |||
for _, driver := range drivers { | |||
if strings.Contains(volume.Driver, driver) { |
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.
The daemon code uses filter.ExactMatch
instead of a substring match here:
https://github.com/moby/moby/blob/a3efe9722f34af5cf4443fe3a5c4e4e3e0457b54/daemon/list.go#L636
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.
makes sense, I will correct that
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.
...done
api/handlers.go
Outdated
for _, container := range c.cluster.Containers() { | ||
for _, mount := range container.Mounts { | ||
if mount.Type == "volume" { | ||
if mount.Driver == "local" { |
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.
Does this mean that the dangling
filter will not work for non-local volumes?
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.
no, it still works for all, local and non-local.
The condition on line 353 is there to honor the "existing" naming convention used around line 417 for local volumes
api/handlers.go
Outdated
@@ -359,6 +383,18 @@ func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
|
|||
found = false | |||
for _, driver := range drivers { | |||
if volume.Driver == driver { |
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.
Is there any reason you can't use filters.ExactMatch
, just like the daemon code?
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.
no reason, I was just following the existing pattern in name
and node
matching, but yes, using ExactMatch is much cleaner.
shall I change the name/node filter matching to mirror the daemon? ie using filters.Match
?
or "if it ain't broke don't fix it"
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.
Yeah, let's leave the name/node filtering alone. Let's keep this PR focused.
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.
sounds good!
PR is updated with ExactMatch
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.
LGTM, but ping @nishanttotla as well
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.
@dani-docker the CI is failing due to go fmt
. Can you format your code using fmt
? Also, could you add a unit test to this PR
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.
For what it's worth, I'd recommend breaking up the getVolumes()
function into helper functions to make it easier to unit test; that way your unit test doesn't have to worry about instantiating http.ResponseWriter
objects, http.Request
objects, etc.
api/handlers.go
Outdated
danglingOnly := false | ||
refVolumes := make(map[string]bool) | ||
|
||
if filters.Include("dangling") { |
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.
Actually I do not like so much ident. Could rewrite this to use return fast to make it much more readable?
api/handlers.go
Outdated
@@ -359,7 +382,13 @@ func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
|
|||
if filters.Include("label") { | |||
if filters.Include("driver") { |
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.
please use gofmt
to format these code.
@@ -359,6 +382,12 @@ func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { | |||
continue | |||
} | |||
|
|||
if filters.Include("driver") { |
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.
Instead of filtering in swarm, wouldn't it be possible to just have the engines do this filtering? The engine API already provides these filters, so should be able to return just what we're looking for?
(I'm not to familiar with the Swarm code-base, but always assumed that's what it did, but I now see all engine's functionality is replicated here)
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.
The filtering is done on locally cached volumes, making calls to all engines in the cluster to filter out accordingly defeats the swarm classic design. Note, this is not specific to Volumes, same applies to Images, Containers, Networks etc...
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.
Swarm classic is designed to always read from the cache (and do its own filtering) on read-only calls. For performance reasons, we cannot do engine calls during read-only operations.
api/handlers.go
Outdated
|
||
if filters.Include("dangling") { | ||
if filters.ExactMatch("dangling", "true") { | ||
danglingOnly = true |
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.
Note that the dangling
filter also supports dangling=false
, which returns all volumes that are not dangling (that case could be handled by passing the filter to the engine, and collect all non-dangling volumes)
see the original change in the engine; https://github.com/moby/moby/pull/21361/files#diff-9320049615863c9b6ab00c749bbb2f09R526
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.
Right, calling the engine directly was one simple option (see PR description), but api calls to all engines in the cluster can be taxing, hence we decided to build the list of used volumes from locally cached containers.
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.
Won't that easily lead to incorrect data (i.e. container may now use the volume, or vice-versa); or not a problem?
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.
good catch @thaJeztah! I will address the above
had a chat with @thaJeztah and 2 valid use cases were identified:
|
While waiting on #2832
if #2832 is approved, I will update this PR with cleaner/simpler code |
api/handlers.go
Outdated
@@ -330,6 +330,39 @@ func getVolume(c *context, w http.ResponseWriter, r *http.Request) { | |||
httpError(w, fmt.Sprintf("No such volume: %s", name), http.StatusNotFound) | |||
} | |||
|
|||
func GetUsedVolumes(containers []*cluster.Container) map[string]bool { |
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 function doesn't need to be public (i.e. it can be named getUsedVolumes
instead of GetUsedVolumes
.
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.
right, the unit test is in the same package, no need for public.
I will fix that
api/handlers.go
Outdated
return usedVolumes | ||
} | ||
|
||
func GetFilteredVolumes(containers []*cluster.Container, volumes []*apitypes.Volume, dangling bool) []*apitypes.Volume { |
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.
As above, make this function private.
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.
right, the unit test is in the same package, no need for public.
I will fix that
api/handlers.go
Outdated
// volume name allowed char [a-zA-Z0-9][a-zA-Z0-9_.-] | ||
// splitting on / is safe to keep the original output intact | ||
stripEngine := func(vol *apitypes.Volume) *apitypes.Volume { | ||
if vol.Driver != "local" { |
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.
Instead of special-casing local volumes, can you just do parts = strings.Split(vol.Name)
and use parts[1]
if len(parts) > 1
and vol.Name
otherwise?
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 have to special case local
volumes so I don't change its engine_name/volume_name
format.
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.
In that case, add a comment.
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.
done
PR updated with @wsong suggestions |
api/handlers.go
Outdated
// volume name allowed char [a-zA-Z0-9][a-zA-Z0-9_.-] | ||
// splitting on / is safe to keep the original output intact | ||
stripEngine := func(vol *apitypes.Volume) *apitypes.Volume { | ||
//don't change local volume names "engine_name/volume_name" |
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 meant that you should add a comment explaining why you're doing this.
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.
updated
@wsong |
api/handlers.go
Outdated
//this naming format is temporarily extended to all volumes to provide uniqueness | ||
// while identifying dangling volumes. Restoring the original naming convention back. | ||
if vol.Driver != "local" { | ||
vol.Name = strings.Split(vol.Name, "/")[1] |
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.
Would it make sense to check that strings.Split(vol.Name, "/")
here has len() > 1
so that we don't panic if there's a problem here?
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.
Possibly SplitN as well (I think with custom drivers, a slash may be allowed in names)
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.
here https://github.com/dani-docker/swarm/blob/117d215985d6d06fb66115dd781e32253fe3aa79/api/handlers.go#L426 guarantees that "/" is appended when dangling filter is in use.
But if you think this helper method will be re-used and to be on the safe, I will make the change.
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.
@thaJeztah
you are correct! when I looked at the code, I only checked local volume, as you stated, custom drivers do allow slash
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.
changes done for both concerns
Signed-off-by: Dani Louca <dani.louca@docker.com>
PR updated to address the / in plugin volume names and safeguard check for len of split. |
volume ls
in swarm classic only handles filters with options "node", "name" and "label".This PR adds support for "driver" and "dangling"
Adding "driver" to the filter option is pretty straightforward.
The "dangling" filter is a bit more involved as the "volume" struct has no "dangling" property.
2 options to support "dangling":
1- add an extra call to the engine to get the dangling volumes
or
2- Iterate through cached "containers" and build a map of volumes that are still referenced by the container, and finally use the delta to delta to determine dangling volumes.
After chatting with @nishanttotla , 1) seems a bit taxing so I decided to go with option 2)
Note: I am not caching the map for referenced volumes, so we don't have to maintain it, besides, the calls to filter based on "dangling" should be fast and should not be that frequent.
Repro Steps for
driver
:1- Have at least 2 volumes using 2 separate drivers.
for ex:
docker volume ls
2- Run
docker volume ls --filter=driver=local
3-
Expect Results
Actual Results (all volumes returned)
Repro Steps for
dangling
:1- Have at least 2 volumes with one volume that is dangling (not used).
(sshvolume in this example is dangling)
docker volume ls
2- Run
docker volume ls --filter=dangling=true
3-
Expect Results
Actual Results (all volumes returned)
Signed-off-by: Dani Louca dani.louca@docker.com