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