2018-03-22 21:11:03 +00:00
package service // import "github.com/docker/docker/volume/service"
2015-09-18 23:58:05 +00:00
import (
2018-03-22 21:11:03 +00:00
"context"
"fmt"
2016-10-05 18:39:45 +00:00
"net"
2016-03-16 21:52:34 +00:00
"os"
"path/filepath"
2015-09-18 23:58:05 +00:00
"sync"
2016-03-16 21:52:34 +00:00
"time"
2015-09-18 23:58:05 +00:00
2018-03-22 21:11:03 +00:00
"github.com/docker/docker/errdefs"
2015-09-18 23:58:05 +00:00
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
2018-04-17 20:50:28 +00:00
volumemounts "github.com/docker/docker/volume/mounts"
2018-03-22 21:11:03 +00:00
"github.com/docker/docker/volume/service/opts"
2020-09-10 20:15:40 +00:00
"github.com/moby/locker"
"github.com/pkg/errors"
2017-07-26 21:42:13 +00:00
"github.com/sirupsen/logrus"
2018-09-11 22:12:55 +00:00
bolt "go.etcd.io/bbolt"
2015-09-18 23:58:05 +00:00
)
2016-03-16 21:52:34 +00:00
const (
2016-12-01 22:17:07 +00:00
volumeDataDir = "volumes"
2016-03-16 21:52:34 +00:00
)
2016-04-11 15:17:52 +00:00
type volumeWrapper struct {
2016-03-16 21:52:34 +00:00
volume . Volume
2016-09-17 19:32:31 +00:00
labels map [ string ] string
scope string
options map [ string ] string
}
func ( v volumeWrapper ) Options ( ) map [ string ] string {
2018-03-13 19:39:23 +00:00
if v . options == nil {
return nil
}
options := make ( map [ string ] string , len ( v . options ) )
2016-09-17 19:32:31 +00:00
for key , value := range v . options {
options [ key ] = value
}
return options
2016-03-16 21:52:34 +00:00
}
2016-04-11 15:17:52 +00:00
func ( v volumeWrapper ) Labels ( ) map [ string ] string {
2018-03-13 19:39:23 +00:00
if v . labels == nil {
return nil
}
labels := make ( map [ string ] string , len ( v . labels ) )
for key , value := range v . labels {
labels [ key ] = value
}
return labels
2016-03-16 21:52:34 +00:00
}
2016-04-11 15:17:52 +00:00
func ( v volumeWrapper ) Scope ( ) string {
return v . scope
}
func ( v volumeWrapper ) CachedPath ( ) string {
if vv , ok := v . Volume . ( interface {
CachedPath ( ) string
} ) ; ok {
return vv . CachedPath ( )
}
return v . Volume . Path ( )
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
// StoreOpt sets options for a VolumeStore
type StoreOpt func ( store * VolumeStore ) error
2018-03-22 21:11:03 +00:00
// NewStore creates a new volume store at the given path
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
func NewStore ( rootPath string , drivers * drivers . Store , opts ... StoreOpt ) ( * VolumeStore , error ) {
2016-03-16 21:52:34 +00:00
vs := & VolumeStore {
2016-09-17 19:32:31 +00:00
locks : & locker . Locker { } ,
names : make ( map [ string ] volume . Volume ) ,
2016-12-05 23:39:05 +00:00
refs : make ( map [ string ] map [ string ] struct { } ) ,
2016-09-17 19:32:31 +00:00
labels : make ( map [ string ] map [ string ] string ) ,
options : make ( map [ string ] map [ string ] string ) ,
2018-03-19 21:18:52 +00:00
drivers : drivers ,
2015-09-18 23:58:05 +00:00
}
2016-03-16 21:52:34 +00:00
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
for _ , o := range opts {
if err := o ( vs ) ; err != nil {
return nil , err
}
}
2016-03-16 21:52:34 +00:00
if rootPath != "" {
// initialize metadata store
volPath := filepath . Join ( rootPath , volumeDataDir )
2018-04-24 14:26:10 +00:00
if err := os . MkdirAll ( volPath , 0750 ) ; err != nil {
2016-03-16 21:52:34 +00:00
return nil , err
}
var err error
2018-03-22 21:11:03 +00:00
vs . db , err = bolt . Open ( filepath . Join ( volPath , "metadata.db" ) , 0600 , & bolt . Options { Timeout : 1 * time . Second } )
2016-03-16 21:52:34 +00:00
if err != nil {
2016-09-23 14:38:19 +00:00
return nil , errors . Wrap ( err , "error while opening volume store metadata database" )
2016-03-16 21:52:34 +00:00
}
// initialize volumes bucket
if err := vs . db . Update ( func ( tx * bolt . Tx ) error {
2016-12-01 22:17:07 +00:00
if _ , err := tx . CreateBucketIfNotExists ( volumeBucketName ) ; err != nil {
2016-09-23 14:38:19 +00:00
return errors . Wrap ( err , "error while setting up volume store metadata database" )
2016-03-16 21:52:34 +00:00
}
return nil
} ) ; err != nil {
return nil , err
}
}
2016-12-01 22:17:07 +00:00
vs . restore ( )
2016-03-16 21:52:34 +00:00
return vs , nil
2015-09-18 23:58:05 +00:00
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
// WithEventLogger configures the VolumeStore with the given VolumeEventLogger
func WithEventLogger ( logger VolumeEventLogger ) StoreOpt {
return func ( store * VolumeStore ) error {
store . eventLogger = logger
return nil
}
}
2016-01-21 03:31:46 +00:00
func ( s * VolumeStore ) getNamed ( name string ) ( volume . Volume , bool ) {
2016-11-16 03:35:39 +00:00
s . globalLock . RLock ( )
2016-01-21 03:31:46 +00:00
v , exists := s . names [ name ]
2016-11-16 03:35:39 +00:00
s . globalLock . RUnlock ( )
2016-01-21 03:31:46 +00:00
return v , exists
2015-10-19 20:43:56 +00:00
}
2016-01-21 03:31:46 +00:00
func ( s * VolumeStore ) setNamed ( v volume . Volume , ref string ) {
2016-12-05 23:39:05 +00:00
name := v . Name ( )
2015-10-19 20:43:56 +00:00
s . globalLock . Lock ( )
2016-12-05 23:39:05 +00:00
s . names [ name ] = v
2015-09-23 20:29:14 +00:00
if len ( ref ) > 0 {
2016-12-05 23:39:05 +00:00
if s . refs [ name ] == nil {
s . refs [ name ] = make ( map [ string ] struct { } )
}
s . refs [ name ] [ ref ] = struct { } { }
2015-09-23 20:29:14 +00:00
}
2015-10-19 20:43:56 +00:00
s . globalLock . Unlock ( )
}
2016-12-05 23:39:05 +00:00
// hasRef returns true if the given name has at least one ref.
// Callers of this function are expected to hold the name lock.
func ( s * VolumeStore ) hasRef ( name string ) bool {
s . globalLock . RLock ( )
l := len ( s . refs [ name ] )
s . globalLock . RUnlock ( )
return l > 0
}
2016-10-05 18:39:45 +00:00
// getRefs gets the list of refs for a given name
// Callers of this function are expected to hold the name lock.
func ( s * VolumeStore ) getRefs ( name string ) [ ] string {
2016-11-16 03:35:39 +00:00
s . globalLock . RLock ( )
2016-12-05 23:39:05 +00:00
defer s . globalLock . RUnlock ( )
refs := make ( [ ] string , 0 , len ( s . refs [ name ] ) )
for r := range s . refs [ name ] {
refs = append ( refs , r )
}
2016-10-05 18:39:45 +00:00
return refs
}
2018-03-22 21:11:03 +00:00
// purge allows the cleanup of internal data on docker in case
2016-06-10 14:40:09 +00:00
// the internal data is out of sync with volumes driver plugins.
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) purge ( ctx context . Context , name string ) error {
2015-10-19 20:43:56 +00:00
s . globalLock . Lock ( )
2018-03-22 21:11:03 +00:00
defer s . globalLock . Unlock ( )
select {
case <- ctx . Done ( ) :
return ctx . Err ( )
default :
}
2016-12-01 22:17:07 +00:00
v , exists := s . names [ name ]
if exists {
2017-11-22 07:10:31 +00:00
driverName := v . DriverName ( )
2018-03-19 21:18:52 +00:00
if _ , err := s . drivers . ReleaseDriver ( driverName ) ; err != nil {
2017-11-22 07:10:31 +00:00
logrus . WithError ( err ) . WithField ( "driver" , driverName ) . Error ( "Error releasing reference to volume driver" )
2016-12-01 22:17:07 +00:00
}
}
if err := s . removeMeta ( name ) ; err != nil {
logrus . Errorf ( "Error removing volume metadata for volume %q: %v" , name , err )
}
2015-09-23 20:29:14 +00:00
delete ( s . names , name )
delete ( s . refs , name )
2016-03-16 21:52:34 +00:00
delete ( s . labels , name )
2016-09-17 19:32:31 +00:00
delete ( s . options , name )
2018-03-22 21:11:03 +00:00
return nil
2015-10-19 20:43:56 +00:00
}
2018-10-31 18:50:24 +00:00
// VolumeStore is responsible for storing and reference counting volumes.
2015-09-18 23:58:05 +00:00
type VolumeStore struct {
2016-11-16 03:35:39 +00:00
// locks ensures that only one action is being performed on a particular volume at a time without locking the entire store
// since actions on volumes can be quite slow, this ensures the store is free to handle requests for other volumes.
2018-03-19 21:18:52 +00:00
locks * locker . Locker
drivers * drivers . Store
2016-11-16 03:35:39 +00:00
// globalLock is used to protect access to mutable structures used by the store object
globalLock sync . RWMutex
2016-11-14 01:55:27 +00:00
// names stores the volume name -> volume relationship.
2015-09-23 20:29:14 +00:00
// This is used for making lookups faster so we don't have to probe all drivers
2016-01-21 03:31:46 +00:00
names map [ string ] volume . Volume
2015-09-23 20:29:14 +00:00
// refs stores the volume name and the list of things referencing it
2016-12-05 23:39:05 +00:00
refs map [ string ] map [ string ] struct { }
2016-03-16 21:52:34 +00:00
// labels stores volume labels for each volume
labels map [ string ] map [ string ] string
2016-09-17 19:32:31 +00:00
// options stores volume options for each volume
options map [ string ] map [ string ] string
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
db * bolt . DB
eventLogger VolumeEventLogger
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
func filterByDriver ( names [ ] string ) filterFunc {
return func ( v volume . Volume ) bool {
for _ , name := range names {
if name == v . DriverName ( ) {
return true
}
}
return false
}
}
func ( s * VolumeStore ) byReferenced ( referenced bool ) filterFunc {
return func ( v volume . Volume ) bool {
return s . hasRef ( v . Name ( ) ) == referenced
}
}
func ( s * VolumeStore ) filter ( ctx context . Context , vols * [ ] volume . Volume , by By ) ( warnings [ ] string , err error ) {
// note that this specifically does not support the `FromList` By type.
switch f := by . ( type ) {
case nil :
if * vols == nil {
var ls [ ] volume . Volume
ls , warnings , err = s . list ( ctx )
if err != nil {
return warnings , err
}
* vols = ls
}
case byDriver :
if * vols != nil {
filter ( vols , filterByDriver ( [ ] string ( f ) ) )
return nil , nil
}
var ls [ ] volume . Volume
ls , warnings , err = s . list ( ctx , [ ] string ( f ) ... )
if err != nil {
return nil , err
}
* vols = ls
case ByReferenced :
// TODO(@cpuguy83): It would be nice to optimize this by looking at the list
// of referenced volumes, however the locking strategy makes this difficult
// without either providing inconsistent data or deadlocks.
if * vols == nil {
var ls [ ] volume . Volume
ls , warnings , err = s . list ( ctx )
if err != nil {
return nil , err
}
* vols = ls
}
filter ( vols , s . byReferenced ( bool ( f ) ) )
case andCombinator :
for _ , by := range f {
w , err := s . filter ( ctx , vols , by )
if err != nil {
return warnings , err
}
warnings = append ( warnings , w ... )
}
case orCombinator :
for _ , by := range f {
switch by . ( type ) {
case byDriver :
var ls [ ] volume . Volume
w , err := s . filter ( ctx , & ls , by )
if err != nil {
return warnings , err
}
warnings = append ( warnings , w ... )
default :
ls , w , err := s . list ( ctx )
if err != nil {
return warnings , err
}
warnings = append ( warnings , w ... )
w , err = s . filter ( ctx , & ls , by )
if err != nil {
return warnings , err
}
warnings = append ( warnings , w ... )
* vols = append ( * vols , ls ... )
}
}
unique ( vols )
case CustomFilter :
if * vols == nil {
var ls [ ] volume . Volume
ls , warnings , err = s . list ( ctx )
if err != nil {
return nil , err
}
* vols = ls
}
filter ( vols , filterFunc ( f ) )
default :
return nil , errdefs . InvalidParameter ( errors . Errorf ( "unsupported filter: %T" , f ) )
}
return warnings , nil
}
func unique ( ls * [ ] volume . Volume ) {
names := make ( map [ string ] bool , len ( * ls ) )
filter ( ls , func ( v volume . Volume ) bool {
if names [ v . Name ( ) ] {
return false
}
names [ v . Name ( ) ] = true
return true
} )
}
// Find lists volumes filtered by the past in filter.
2016-03-09 16:17:57 +00:00
// If a driver returns a volume that has name which conflicts with another volume from a different driver,
2015-09-23 20:29:14 +00:00
// the first volume is chosen and the conflicting volume is dropped.
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) Find ( ctx context . Context , by By ) ( vols [ ] volume . Volume , warnings [ ] string , err error ) {
logrus . WithField ( "ByType" , fmt . Sprintf ( "%T" , by ) ) . WithField ( "ByValue" , fmt . Sprintf ( "%+v" , by ) ) . Debug ( "VolumeStore.Find" )
switch f := by . ( type ) {
case nil , orCombinator , andCombinator , byDriver , ByReferenced , CustomFilter :
warnings , err = s . filter ( ctx , & vols , by )
case fromList :
warnings , err = s . filter ( ctx , f . ls , f . by )
default :
// Really shouldn't be possible, but makes sure that any new By's are added to this check.
err = errdefs . InvalidParameter ( errors . Errorf ( "unsupported filter type: %T" , f ) )
}
2015-09-23 20:29:14 +00:00
if err != nil {
return nil , nil , & OpErr { Err : err , Op : "list" }
}
2018-03-22 21:11:03 +00:00
2015-09-23 20:29:14 +00:00
var out [ ] volume . Volume
2015-09-18 23:58:05 +00:00
for _ , v := range vols {
2017-08-22 22:25:31 +00:00
name := normalizeVolumeName ( v . Name ( ) )
2015-09-23 20:29:14 +00:00
s . locks . Lock ( name )
2016-01-21 03:31:46 +00:00
storedV , exists := s . getNamed ( name )
2016-03-22 20:24:09 +00:00
// Note: it's not safe to populate the cache here because the volume may have been
// deleted before we acquire a lock on its name
2016-01-21 03:31:46 +00:00
if exists && storedV . DriverName ( ) != v . DriverName ( ) {
logrus . Warnf ( "Volume name %s already exists for driver %s, not including volume returned by %s" , v . Name ( ) , storedV . DriverName ( ) , v . DriverName ( ) )
2015-09-23 20:29:14 +00:00
s . locks . Unlock ( v . Name ( ) )
continue
}
out = append ( out , v )
s . locks . Unlock ( v . Name ( ) )
2015-09-18 23:58:05 +00:00
}
2015-09-23 20:29:14 +00:00
return out , warnings , nil
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
type filterFunc func ( volume . Volume ) bool
func filter ( vols * [ ] volume . Volume , fn filterFunc ) {
var evict [ ] int
for i , v := range * vols {
if ! fn ( v ) {
evict = append ( evict , i )
}
}
for n , i := range evict {
copy ( ( * vols ) [ i - n : ] , ( * vols ) [ i - n + 1 : ] )
( * vols ) [ len ( * vols ) - 1 ] = nil
* vols = ( * vols ) [ : len ( * vols ) - 1 ]
}
}
2015-09-23 20:29:14 +00:00
// list goes through each volume driver and asks for its list of volumes.
2018-03-22 21:11:03 +00:00
// TODO(@cpuguy83): plumb context through
func ( s * VolumeStore ) list ( ctx context . Context , driverNames ... string ) ( [ ] volume . Volume , [ ] string , error ) {
2015-09-23 20:29:14 +00:00
var (
2018-03-22 21:11:03 +00:00
ls = [ ] volume . Volume { } // do not return a nil value as this affects filtering
2015-09-23 20:29:14 +00:00
warnings [ ] string
)
2018-03-22 21:11:03 +00:00
var dls [ ] volume . Driver
all , err := s . drivers . GetAllDrivers ( )
2016-05-16 15:50:55 +00:00
if err != nil {
return nil , nil , err
}
2018-03-22 21:11:03 +00:00
if len ( driverNames ) == 0 {
dls = all
} else {
idx := make ( map [ string ] bool , len ( driverNames ) )
for _ , name := range driverNames {
idx [ name ] = true
}
for _ , d := range all {
if idx [ d . Name ( ) ] {
dls = append ( dls , d )
}
}
}
2016-05-16 15:50:55 +00:00
2015-09-23 20:29:14 +00:00
type vols struct {
2016-01-21 03:31:46 +00:00
vols [ ] volume . Volume
err error
driverName string
2015-09-23 20:29:14 +00:00
}
2018-03-22 21:11:03 +00:00
chVols := make ( chan vols , len ( dls ) )
2015-09-23 20:29:14 +00:00
2018-03-22 21:11:03 +00:00
for _ , vd := range dls {
2015-09-23 20:29:14 +00:00
go func ( d volume . Driver ) {
vs , err := d . List ( )
if err != nil {
2016-01-21 03:31:46 +00:00
chVols <- vols { driverName : d . Name ( ) , err : & OpErr { Err : err , Name : d . Name ( ) , Op : "list" } }
2015-09-23 20:29:14 +00:00
return
}
2016-04-11 15:17:52 +00:00
for i , v := range vs {
2016-11-16 03:35:39 +00:00
s . globalLock . RLock ( )
2016-09-17 19:32:31 +00:00
vs [ i ] = volumeWrapper { v , s . labels [ v . Name ( ) ] , d . Scope ( ) , s . options [ v . Name ( ) ] }
2016-11-16 03:35:39 +00:00
s . globalLock . RUnlock ( )
2016-04-11 15:17:52 +00:00
}
2015-09-23 20:29:14 +00:00
chVols <- vols { vols : vs }
} ( vd )
}
2016-01-21 03:31:46 +00:00
badDrivers := make ( map [ string ] struct { } )
2018-03-22 21:11:03 +00:00
for i := 0 ; i < len ( dls ) ; i ++ {
2015-09-23 20:29:14 +00:00
vs := <- chVols
if vs . err != nil {
warnings = append ( warnings , vs . err . Error ( ) )
2016-01-21 03:31:46 +00:00
badDrivers [ vs . driverName ] = struct { } { }
2015-09-23 20:29:14 +00:00
}
ls = append ( ls , vs . vols ... )
}
2016-01-21 03:31:46 +00:00
if len ( badDrivers ) > 0 {
2016-11-16 03:35:39 +00:00
s . globalLock . RLock ( )
2016-01-21 03:31:46 +00:00
for _ , v := range s . names {
if _ , exists := badDrivers [ v . DriverName ( ) ] ; exists {
ls = append ( ls , v )
}
}
2016-11-16 03:35:39 +00:00
s . globalLock . RUnlock ( )
2016-01-21 03:31:46 +00:00
}
2015-09-23 20:29:14 +00:00
return ls , warnings , nil
}
2018-03-22 21:11:03 +00:00
// Create creates a volume with the given name and driver
// If the volume needs to be created with a reference to prevent race conditions
// with volume cleanup, make sure to use the `CreateWithReference` option.
func ( s * VolumeStore ) Create ( ctx context . Context , name , driverName string , createOpts ... opts . CreateOption ) ( volume . Volume , error ) {
var cfg opts . CreateConfig
for _ , o := range createOpts {
o ( & cfg )
}
2017-08-22 22:25:31 +00:00
name = normalizeVolumeName ( name )
2015-10-19 20:43:56 +00:00
s . locks . Lock ( name )
defer s . locks . Unlock ( name )
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
return nil , ctx . Err ( )
default :
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
v , created , err := s . create ( ctx , name , driverName , cfg . Options , cfg . Labels )
2015-09-23 20:29:14 +00:00
if err != nil {
2016-11-22 08:44:46 +00:00
if _ , ok := err . ( * OpErr ) ; ok {
return nil , err
}
2015-09-23 20:29:14 +00:00
return nil , & OpErr { Err : err , Name : name , Op : "create" }
2015-09-18 23:58:05 +00:00
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
if created && s . eventLogger != nil {
s . eventLogger . LogVolumeEvent ( v . Name ( ) , "create" , map [ string ] string { "driver" : v . DriverName ( ) } )
}
2018-03-22 21:11:03 +00:00
s . setNamed ( v , cfg . Reference )
2015-09-23 20:29:14 +00:00
return v , nil
}
2016-10-05 18:39:45 +00:00
// checkConflict checks the local cache for name collisions with the passed in name,
// for existing volumes with the same name but in a different driver.
// This is used by `Create` as a best effort to prevent name collisions for volumes.
// If a matching volume is found that is not a conflict that is returned so the caller
// does not need to perform an additional lookup.
// When no matching volume is found, both returns will be nil
//
// Note: This does not probe all the drivers for name collisions because v1 plugins
// are very slow, particularly if the plugin is down, and cause other issues,
// particularly around locking the store.
// TODO(cpuguy83): With v2 plugins this shouldn't be a problem. Could also potentially
// use a connect timeout for this kind of check to ensure we aren't blocking for a
// long time.
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) checkConflict ( ctx context . Context , name , driverName string ) ( volume . Volume , error ) {
2016-10-05 18:39:45 +00:00
// check the local cache
v , _ := s . getNamed ( name )
2016-11-30 16:11:50 +00:00
if v == nil {
return nil , nil
}
2016-10-05 18:39:45 +00:00
2016-11-30 16:11:50 +00:00
vDriverName := v . DriverName ( )
var conflict bool
2016-12-14 01:20:52 +00:00
if driverName != "" {
// Retrieve canonical driver name to avoid inconsistencies (for example
// "plugin" vs. "plugin:latest")
2018-03-19 21:18:52 +00:00
vd , err := s . drivers . GetDriver ( driverName )
2016-12-14 01:20:52 +00:00
if err != nil {
return nil , err
}
if vDriverName != vd . Name ( ) {
conflict = true
}
2016-11-30 16:11:50 +00:00
}
2016-10-05 18:39:45 +00:00
2016-11-30 16:11:50 +00:00
// let's check if the found volume ref
// is stale by checking with the driver if it still exists
2018-03-22 21:11:03 +00:00
exists , err := volumeExists ( ctx , s . drivers , v )
2016-11-30 16:11:50 +00:00
if err != nil {
return nil , errors . Wrapf ( errNameConflict , "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v" , name , vDriverName , err )
}
if exists {
if conflict {
return nil , errors . Wrapf ( errNameConflict , "driver '%s' already has volume '%s'" , vDriverName , name )
2016-10-05 18:39:45 +00:00
}
return v , nil
}
2016-12-05 23:39:05 +00:00
if s . hasRef ( v . Name ( ) ) {
2016-11-30 16:11:50 +00:00
// Containers are referencing this volume but it doesn't seem to exist anywhere.
// Return a conflict error here, the user can fix this with `docker volume rm -f`
return nil , errors . Wrapf ( errNameConflict , "found references to volume '%s' in driver '%s' but the volume was not found in the driver -- you may need to remove containers referencing this volume or force remove the volume to re-create it" , name , vDriverName )
}
// doesn't exist, so purge it from the cache
2018-03-22 21:11:03 +00:00
s . purge ( ctx , name )
2016-10-05 18:39:45 +00:00
return nil , nil
}
2016-11-30 16:11:50 +00:00
// volumeExists returns if the volume is still present in the driver.
// An error is returned if there was an issue communicating with the driver.
2018-03-22 21:11:03 +00:00
func volumeExists ( ctx context . Context , store * drivers . Store , v volume . Volume ) ( bool , error ) {
exists , err := lookupVolume ( ctx , store , v . DriverName ( ) , v . Name ( ) )
2016-11-30 16:11:50 +00:00
if err != nil {
2016-12-01 22:17:07 +00:00
return false , err
2016-11-30 16:11:50 +00:00
}
2016-12-01 22:17:07 +00:00
return exists != nil , nil
2016-11-30 16:11:50 +00:00
}
2015-09-23 20:29:14 +00:00
// create asks the given driver to create a volume with the name/opts.
// If a volume with the name is already known, it will ask the stored driver for the volume.
2016-10-05 18:39:45 +00:00
// If the passed in driver name does not match the driver name which is stored
// for the given volume name, an error is returned after checking if the reference is stale.
// If the reference is stale, it will be purged and this create can continue.
2016-02-11 23:21:52 +00:00
// It is expected that callers of this function hold any necessary locks.
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
func ( s * VolumeStore ) create ( ctx context . Context , name , driverName string , opts , labels map [ string ] string ) ( volume . Volume , bool , error ) {
2015-09-10 02:23:06 +00:00
// Validate the name in a platform-specific manner
2017-08-01 17:32:44 +00:00
// volume name validation is specific to the host os and not on container image
2021-06-11 19:01:18 +00:00
parser := volumemounts . NewParser ( )
2017-08-01 17:32:44 +00:00
err := parser . ValidateVolumeName ( name )
2015-09-10 02:23:06 +00:00
if err != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return nil , false , err
2015-09-10 02:23:06 +00:00
}
2018-03-22 21:11:03 +00:00
v , err := s . checkConflict ( ctx , name , driverName )
2016-10-05 18:39:45 +00:00
if err != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return nil , false , err
2016-10-05 18:39:45 +00:00
}
2016-11-30 16:11:50 +00:00
2016-10-05 18:39:45 +00:00
if v != nil {
2017-09-19 18:34:41 +00:00
// there is an existing volume, if we already have this stored locally, return it.
// TODO: there could be some inconsistent details such as labels here
if vv , _ := s . getNamed ( v . Name ( ) ) ; vv != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return vv , false , nil
2017-09-19 18:34:41 +00:00
}
2015-09-23 20:29:14 +00:00
}
2016-02-10 17:02:52 +00:00
// Since there isn't a specified driver name, let's see if any of the existing drivers have this volume name
if driverName == "" {
2018-03-22 21:11:03 +00:00
v , _ = s . getVolume ( ctx , name , "" )
2016-02-10 17:02:52 +00:00
if v != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return v , false , nil
2016-02-10 17:02:52 +00:00
}
}
2018-03-19 21:18:52 +00:00
if driverName == "" {
driverName = volume . DefaultDriverName
}
vd , err := s . drivers . CreateDriver ( driverName )
2015-09-18 23:58:05 +00:00
if err != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return nil , false , & OpErr { Op : "create" , Name : name , Err : err }
2015-09-18 23:58:05 +00:00
}
2016-03-03 07:23:49 +00:00
logrus . Debugf ( "Registering new volume reference: driver %q, name %q" , vd . Name ( ) , name )
2017-09-19 18:34:41 +00:00
if v , _ = vd . Get ( name ) ; v == nil {
v , err = vd . Create ( name , opts )
if err != nil {
2018-03-19 21:18:52 +00:00
if _ , err := s . drivers . ReleaseDriver ( driverName ) ; err != nil {
2017-09-19 18:34:41 +00:00
logrus . WithError ( err ) . WithField ( "driver" , driverName ) . Error ( "Error releasing reference to volume driver" )
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return nil , false , err
2017-10-20 19:39:47 +00:00
}
2016-03-16 21:52:34 +00:00
}
2017-09-19 18:34:41 +00:00
2016-03-29 17:46:29 +00:00
s . globalLock . Lock ( )
2016-03-16 21:52:34 +00:00
s . labels [ name ] = labels
2016-09-17 19:32:31 +00:00
s . options [ name ] = opts
2016-12-05 23:39:05 +00:00
s . refs [ name ] = make ( map [ string ] struct { } )
2016-03-29 17:46:29 +00:00
s . globalLock . Unlock ( )
2016-03-16 21:52:34 +00:00
2016-12-01 22:17:07 +00:00
metadata := volumeMetadata {
Name : name ,
Driver : vd . Name ( ) ,
Labels : labels ,
Options : opts ,
2016-03-16 21:52:34 +00:00
}
2016-12-01 22:17:07 +00:00
if err := s . setMeta ( name , metadata ) ; err != nil {
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return nil , true , err
2016-12-01 22:17:07 +00:00
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
return volumeWrapper { v , labels , vd . Scope ( ) , opts } , true , nil
2015-09-18 23:58:05 +00:00
}
2015-09-23 20:29:14 +00:00
// Get looks if a volume with the given name exists and returns it if so
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) Get ( ctx context . Context , name string , getOptions ... opts . GetOption ) ( volume . Volume , error ) {
var cfg opts . GetConfig
for _ , o := range getOptions {
o ( & cfg )
}
2017-08-22 22:25:31 +00:00
name = normalizeVolumeName ( name )
2015-10-19 20:43:56 +00:00
s . locks . Lock ( name )
defer s . locks . Unlock ( name )
2018-03-22 21:11:03 +00:00
v , err := s . getVolume ( ctx , name , cfg . Driver )
2015-09-23 20:29:14 +00:00
if err != nil {
return nil , & OpErr { Err : err , Name : name , Op : "get" }
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
if cfg . Driver != "" && v . DriverName ( ) != cfg . Driver {
return nil , & OpErr { Name : name , Op : "get" , Err : errdefs . Conflict ( errors . New ( "found volume driver does not match passed in driver" ) ) }
}
s . setNamed ( v , cfg . Reference )
2015-09-23 20:29:14 +00:00
return v , nil
}
2015-09-18 23:58:05 +00:00
2016-03-09 16:17:57 +00:00
// getVolume requests the volume, if the driver info is stored it just accesses that driver,
2015-09-23 20:29:14 +00:00
// if the driver is unknown it probes all drivers until it finds the first volume with that name.
2016-02-11 23:21:52 +00:00
// it is expected that callers of this function hold any necessary locks
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) getVolume ( ctx context . Context , name , driverName string ) ( volume . Volume , error ) {
2016-12-01 22:17:07 +00:00
var meta volumeMetadata
meta , err := s . getMeta ( name )
if err != nil {
return nil , err
}
2016-03-16 21:52:34 +00:00
2018-03-22 21:11:03 +00:00
if driverName != "" {
if meta . Driver == "" {
meta . Driver = driverName
}
if driverName != meta . Driver {
return nil , errdefs . Conflict ( errors . New ( "provided volume driver does not match stored driver" ) )
}
}
if driverName == "" {
driverName = meta . Driver
}
2016-12-01 22:17:07 +00:00
if driverName == "" {
s . globalLock . RLock ( )
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
s . globalLock . RUnlock ( )
return nil , ctx . Err ( )
default :
}
2016-12-01 22:17:07 +00:00
v , exists := s . names [ name ]
s . globalLock . RUnlock ( )
if exists {
meta . Driver = v . DriverName ( )
if err := s . setMeta ( name , meta ) ; err != nil {
return nil , err
2016-03-16 21:52:34 +00:00
}
}
}
2016-12-01 22:17:07 +00:00
if meta . Driver != "" {
2018-03-22 21:11:03 +00:00
vol , err := lookupVolume ( ctx , s . drivers , meta . Driver , name )
2015-09-23 20:29:14 +00:00
if err != nil {
return nil , err
}
2016-12-01 22:17:07 +00:00
if vol == nil {
2018-03-22 21:11:03 +00:00
s . purge ( ctx , name )
2016-12-01 22:17:07 +00:00
return nil , errNoSuchVolume
2016-03-16 21:52:34 +00:00
}
2016-12-01 22:17:07 +00:00
var scope string
2018-03-19 21:18:52 +00:00
vd , err := s . drivers . GetDriver ( meta . Driver )
2016-12-01 22:17:07 +00:00
if err == nil {
scope = vd . Scope ( )
}
return volumeWrapper { vol , meta . Labels , scope , meta . Options } , nil
2015-09-18 23:58:05 +00:00
}
2015-09-23 20:29:14 +00:00
logrus . Debugf ( "Probing all drivers for volume with name: %s" , name )
2018-03-19 21:18:52 +00:00
drivers , err := s . drivers . GetAllDrivers ( )
2015-09-18 23:58:05 +00:00
if err != nil {
2015-09-23 20:29:14 +00:00
return nil , err
2015-09-18 23:58:05 +00:00
}
2015-10-19 20:43:56 +00:00
2015-09-23 20:29:14 +00:00
for _ , d := range drivers {
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
return nil , ctx . Err ( )
default :
}
2015-09-23 20:29:14 +00:00
v , err := d . Get ( name )
2016-12-01 22:17:07 +00:00
if err != nil || v == nil {
2015-09-23 20:29:14 +00:00
continue
}
2016-12-01 22:17:07 +00:00
meta . Driver = v . DriverName ( )
if err := s . setMeta ( name , meta ) ; err != nil {
return nil , err
}
return volumeWrapper { v , meta . Labels , d . Scope ( ) , meta . Options } , nil
2015-09-23 20:29:14 +00:00
}
return nil , errNoSuchVolume
2015-09-18 23:58:05 +00:00
}
2016-12-01 22:17:07 +00:00
// lookupVolume gets the specified volume from the specified driver.
// This will only return errors related to communications with the driver.
// If the driver returns an error that is not communication related the
// error is logged but not returned.
// If the volume is not found it will return `nil, nil``
2018-03-22 21:11:03 +00:00
// TODO(@cpuguy83): plumb through the context to lower level components
func lookupVolume ( ctx context . Context , store * drivers . Store , driverName , volumeName string ) ( volume . Volume , error ) {
2018-03-19 21:18:52 +00:00
if driverName == "" {
driverName = volume . DefaultDriverName
}
vd , err := store . GetDriver ( driverName )
2016-12-01 22:17:07 +00:00
if err != nil {
return nil , errors . Wrapf ( err , "error while checking if volume %q exists in driver %q" , volumeName , driverName )
}
v , err := vd . Get ( volumeName )
if err != nil {
2020-04-17 10:01:01 +00:00
var nErr net . Error
if errors . As ( err , & nErr ) {
2017-03-24 17:51:22 +00:00
if v != nil {
volumeName = v . Name ( )
driverName = v . DriverName ( )
}
return nil , errors . Wrapf ( err , "error while checking if volume %q exists in driver %q" , volumeName , driverName )
2016-12-01 22:17:07 +00:00
}
// At this point, the error could be anything from the driver, such as "no such volume"
// Let's not check an error here, and instead check if the driver returned a volume
2018-03-22 21:11:03 +00:00
logrus . WithError ( err ) . WithField ( "driver" , driverName ) . WithField ( "volume" , volumeName ) . Debug ( "Error while looking up volume" )
2016-12-01 22:17:07 +00:00
}
return v , nil
}
2015-09-23 20:29:14 +00:00
// Remove removes the requested volume. A volume is not removed if it has any refs
2018-03-22 21:11:03 +00:00
func ( s * VolumeStore ) Remove ( ctx context . Context , v volume . Volume , rmOpts ... opts . RemoveOption ) error {
var cfg opts . RemoveConfig
for _ , o := range rmOpts {
o ( & cfg )
}
name := v . Name ( )
2015-10-19 20:43:56 +00:00
s . locks . Lock ( name )
defer s . locks . Unlock ( name )
2015-09-18 23:58:05 +00:00
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
return ctx . Err ( )
default :
}
2016-12-05 23:39:05 +00:00
if s . hasRef ( name ) {
2018-03-22 21:11:03 +00:00
return & OpErr { Err : errVolumeInUse , Name : name , Op : "remove" , Refs : s . getRefs ( name ) }
}
v , err := s . getVolume ( ctx , name , v . DriverName ( ) )
if err != nil {
return err
2015-09-18 23:58:05 +00:00
}
2018-03-19 21:18:52 +00:00
vd , err := s . drivers . GetDriver ( v . DriverName ( ) )
2015-09-23 20:29:14 +00:00
if err != nil {
2017-04-04 13:18:50 +00:00
return & OpErr { Err : err , Name : v . DriverName ( ) , Op : "remove" }
2015-09-18 23:58:05 +00:00
}
2015-09-23 20:29:14 +00:00
logrus . Debugf ( "Removing volume reference: driver %s, name %s" , v . DriverName ( ) , name )
2016-04-11 15:17:52 +00:00
vol := unwrapVolume ( v )
2018-03-22 21:11:03 +00:00
err = vd . Remove ( vol )
if err != nil {
err = & OpErr { Err : err , Name : name , Op : "remove" }
2015-09-18 23:58:05 +00:00
}
2015-09-23 20:29:14 +00:00
2018-03-22 21:11:03 +00:00
if err == nil || cfg . PurgeOnError {
if e := s . purge ( ctx , name ) ; e != nil && err == nil {
err = e
}
}
volumes: only send "create" event when actually creating volume
The VolumesService did not have information wether or not a volume
was _created_ or if a volume already existed in the driver, and
the existing volume was used.
As a result, multiple "create" events could be generated for the
same volume. For example:
1. Run `docker events` in a shell to start listening for events
2. Create a volume:
docker volume create myvolume
3. Start a container that uses that volume:
docker run -dit -v myvolume:/foo busybox
4. Check the events that were generated:
2021-02-15T18:49:55.874621004+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.442759052+01:00 volume create myvolume (driver=local)
2021-02-15T18:50:11.487104176+01:00 container create 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
2021-02-15T18:50:11.519288102+01:00 network connect a19f6bb8d44ff84d478670fa4e34c5bf5305f42786294d3d90e790ac74b6d3e0 (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, name=bridge, type=bridge)
2021-02-15T18:50:11.526407799+01:00 volume mount myvolume (container=45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1, destination=/foo, driver=local, propagation=, read/write=true)
2021-02-15T18:50:11.864134043+01:00 container start 45112157c8b1382626bf5e01ef18445a4c680f3846c5e32d01775dddee8ca6d1 (image=busybox, name=gracious_hypatia)
5. Notice that a "volume create" event is created twice;
- once when `docker volume create` was ran
- once when `docker run ...` was ran
This patch moves the generation of (most) events to the volume _store_, and only
generates an event if the volume did not yet exist.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-02-15 17:45:15 +00:00
if err == nil && s . eventLogger != nil {
s . eventLogger . LogVolumeEvent ( v . Name ( ) , "destroy" , map [ string ] string { "driver" : v . DriverName ( ) } )
}
2018-03-22 21:11:03 +00:00
return err
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
// Release releases the specified reference to the volume
func ( s * VolumeStore ) Release ( ctx context . Context , name string , ref string ) error {
2016-12-05 23:39:05 +00:00
s . locks . Lock ( name )
defer s . locks . Unlock ( name )
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
return ctx . Err ( )
default :
}
2015-10-19 20:43:56 +00:00
2015-09-23 20:29:14 +00:00
s . globalLock . Lock ( )
2016-01-27 08:34:10 +00:00
defer s . globalLock . Unlock ( )
2015-09-18 23:58:05 +00:00
2018-03-22 21:11:03 +00:00
select {
case <- ctx . Done ( ) :
return ctx . Err ( )
default :
}
2016-12-05 23:39:05 +00:00
if s . refs [ name ] != nil {
delete ( s . refs [ name ] , ref )
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
return nil
2015-09-18 23:58:05 +00:00
}
2018-03-22 21:11:03 +00:00
// CountReferences gives a count of all references for a given volume.
func ( s * VolumeStore ) CountReferences ( v volume . Volume ) int {
name := normalizeVolumeName ( v . Name ( ) )
2016-12-05 23:39:05 +00:00
s . locks . Lock ( name )
defer s . locks . Unlock ( name )
2018-03-22 21:11:03 +00:00
s . globalLock . Lock ( )
defer s . globalLock . Unlock ( )
2016-01-08 02:38:38 +00:00
2018-03-22 21:11:03 +00:00
return len ( s . refs [ name ] )
2015-09-18 23:58:05 +00:00
}
2016-03-16 21:52:34 +00:00
2016-04-11 15:17:52 +00:00
func unwrapVolume ( v volume . Volume ) volume . Volume {
if vol , ok := v . ( volumeWrapper ) ; ok {
2016-03-16 21:52:34 +00:00
return vol . Volume
}
return v
}
2016-12-01 22:17:07 +00:00
// Shutdown releases all resources used by the volume store
// It does not make any changes to volumes, drivers, etc.
func ( s * VolumeStore ) Shutdown ( ) error {
return s . db . Close ( )
}