github.com/xiaobinqt/libcompose@v1.1.0/docker/volume/volume.go (about) 1 package volume 2 3 import ( 4 "fmt" 5 6 "github.com/docker/docker/api/types" 7 "github.com/docker/docker/api/types/volume" 8 "github.com/docker/docker/client" 9 "github.com/xiaobinqt/libcompose/config" 10 composeclient "github.com/xiaobinqt/libcompose/docker/client" 11 "github.com/xiaobinqt/libcompose/project" 12 "golang.org/x/net/context" 13 ) 14 15 // Volume holds attributes and method for a volume definition in compose 16 type Volume struct { 17 client client.VolumeAPIClient 18 projectName string 19 name string 20 driver string 21 driverOptions map[string]string 22 external bool 23 // TODO (shouze) missing labels 24 } 25 26 func (v *Volume) fullName() string { 27 name := v.projectName + "_" + v.name 28 if v.external { 29 name = v.name 30 } 31 return name 32 } 33 34 // Inspect inspect the current volume 35 func (v *Volume) Inspect(ctx context.Context) (types.Volume, error) { 36 return v.client.VolumeInspect(ctx, v.fullName()) 37 } 38 39 // Remove removes the current volume (from docker engine) 40 func (v *Volume) Remove(ctx context.Context) error { 41 if v.external { 42 fmt.Printf("Volume %s is external, skipping", v.fullName()) 43 return nil 44 } 45 fmt.Printf("Removing volume %q\n", v.fullName()) 46 return v.client.VolumeRemove(ctx, v.fullName(), true) 47 } 48 49 // EnsureItExists make sure the volume exists and return an error if it does not exists 50 // and cannot be created. 51 func (v *Volume) EnsureItExists(ctx context.Context) error { 52 volumeResource, err := v.Inspect(ctx) 53 if v.external { 54 if client.IsErrNotFound(err) { 55 // FIXME(shouze) introduce some libcompose error type 56 return fmt.Errorf("Volume %s declared as external, but could not be found. Please create the volume manually using docker volume create %s and try again", v.name, v.name) 57 } 58 return err 59 } 60 if err != nil && client.IsErrNotFound(err) { 61 return v.create(ctx) 62 } 63 if volumeResource.Driver != v.driver { 64 return fmt.Errorf("Volume %q needs to be recreated - driver has changed", v.name) 65 } 66 return err 67 } 68 69 func (v *Volume) create(ctx context.Context) error { 70 fmt.Printf("Creating volume %q with driver %q\n", v.fullName(), v.driver) 71 _, err := v.client.VolumeCreate(ctx, volume.VolumeCreateBody{ 72 Name: v.fullName(), 73 Driver: v.driver, 74 DriverOpts: v.driverOptions, 75 // TODO (shouze) missing labels 76 }) 77 78 return err 79 } 80 81 // NewVolume creates a new volume from the specified name and config. 82 func NewVolume(projectName, name string, config *config.VolumeConfig, client client.VolumeAPIClient) *Volume { 83 vol := &Volume{ 84 client: client, 85 projectName: projectName, 86 name: name, 87 } 88 if config != nil { 89 vol.driver = config.Driver 90 vol.driverOptions = config.DriverOpts 91 vol.external = config.External.External 92 93 } 94 return vol 95 } 96 97 // Volumes holds a list of volume 98 type Volumes struct { 99 volumes []*Volume 100 volumeEnabled bool 101 } 102 103 // Initialize make sure volume exists if volume is enabled 104 func (v *Volumes) Initialize(ctx context.Context) error { 105 if !v.volumeEnabled { 106 return nil 107 } 108 for _, volume := range v.volumes { 109 err := volume.EnsureItExists(ctx) 110 if err != nil { 111 return err 112 } 113 } 114 return nil 115 } 116 117 // Remove removes volumes (clean-up) 118 func (v *Volumes) Remove(ctx context.Context) error { 119 if !v.volumeEnabled { 120 return nil 121 } 122 for _, volume := range v.volumes { 123 err := volume.Remove(ctx) 124 if err != nil { 125 return err 126 } 127 } 128 return nil 129 } 130 131 // VolumesFromServices creates a new Volumes struct based on volumes configurations and 132 // services configuration. If a volume is defined but not used by any service, it will return 133 // an error along the Volumes. 134 func VolumesFromServices(cli client.VolumeAPIClient, projectName string, volumeConfigs map[string]*config.VolumeConfig, services *config.ServiceConfigs, volumeEnabled bool) (*Volumes, error) { 135 var err error 136 volumes := make([]*Volume, 0, len(volumeConfigs)) 137 for name, config := range volumeConfigs { 138 volume := NewVolume(projectName, name, config, cli) 139 volumes = append(volumes, volume) 140 } 141 return &Volumes{ 142 volumes: volumes, 143 volumeEnabled: volumeEnabled, 144 }, err 145 } 146 147 // DockerFactory implements project.VolumesFactory 148 type DockerFactory struct { 149 ClientFactory composeclient.Factory 150 } 151 152 // Create implements project.VolumesFactory Create method. 153 // It creates a Volumes (that implements project.Volumes) from specified configurations. 154 func (f *DockerFactory) Create(projectName string, volumeConfigs map[string]*config.VolumeConfig, serviceConfigs *config.ServiceConfigs, volumeEnabled bool) (project.Volumes, error) { 155 cli := f.ClientFactory.Create(nil) 156 return VolumesFromServices(cli, projectName, volumeConfigs, serviceConfigs, volumeEnabled) 157 }