Files
traefik/vendor/github.com/docker/libcompose/docker/volume/volume.go
2018-01-22 12:16:03 +01:00

158 lines
4.5 KiB
Go

package volume
import (
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/docker/libcompose/config"
composeclient "github.com/docker/libcompose/docker/client"
"github.com/docker/libcompose/project"
"golang.org/x/net/context"
)
// Volume holds attributes and method for a volume definition in compose
type Volume struct {
client client.VolumeAPIClient
projectName string
name string
driver string
driverOptions map[string]string
external bool
// TODO (shouze) missing labels
}
func (v *Volume) fullName() string {
name := v.projectName + "_" + v.name
if v.external {
name = v.name
}
return name
}
// Inspect inspect the current volume
func (v *Volume) Inspect(ctx context.Context) (types.Volume, error) {
return v.client.VolumeInspect(ctx, v.fullName())
}
// Remove removes the current volume (from docker engine)
func (v *Volume) Remove(ctx context.Context) error {
if v.external {
fmt.Printf("Volume %s is external, skipping", v.fullName())
return nil
}
fmt.Printf("Removing volume %q\n", v.fullName())
return v.client.VolumeRemove(ctx, v.fullName(), true)
}
// EnsureItExists make sure the volume exists and return an error if it does not exists
// and cannot be created.
func (v *Volume) EnsureItExists(ctx context.Context) error {
volumeResource, err := v.Inspect(ctx)
if v.external {
if client.IsErrNotFound(err) {
// FIXME(shouze) introduce some libcompose error type
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)
}
return err
}
if err != nil && client.IsErrNotFound(err) {
return v.create(ctx)
}
if volumeResource.Driver != v.driver {
return fmt.Errorf("Volume %q needs to be recreated - driver has changed", v.name)
}
return err
}
func (v *Volume) create(ctx context.Context) error {
fmt.Printf("Creating volume %q with driver %q\n", v.fullName(), v.driver)
_, err := v.client.VolumeCreate(ctx, volume.VolumesCreateBody{
Name: v.fullName(),
Driver: v.driver,
DriverOpts: v.driverOptions,
// TODO (shouze) missing labels
})
return err
}
// NewVolume creates a new volume from the specified name and config.
func NewVolume(projectName, name string, config *config.VolumeConfig, client client.VolumeAPIClient) *Volume {
vol := &Volume{
client: client,
projectName: projectName,
name: name,
}
if config != nil {
vol.driver = config.Driver
vol.driverOptions = config.DriverOpts
vol.external = config.External.External
}
return vol
}
// Volumes holds a list of volume
type Volumes struct {
volumes []*Volume
volumeEnabled bool
}
// Initialize make sure volume exists if volume is enabled
func (v *Volumes) Initialize(ctx context.Context) error {
if !v.volumeEnabled {
return nil
}
for _, volume := range v.volumes {
err := volume.EnsureItExists(ctx)
if err != nil {
return err
}
}
return nil
}
// Remove removes volumes (clean-up)
func (v *Volumes) Remove(ctx context.Context) error {
if !v.volumeEnabled {
return nil
}
for _, volume := range v.volumes {
err := volume.Remove(ctx)
if err != nil {
return err
}
}
return nil
}
// VolumesFromServices creates a new Volumes struct based on volumes configurations and
// services configuration. If a volume is defined but not used by any service, it will return
// an error along the Volumes.
func VolumesFromServices(cli client.VolumeAPIClient, projectName string, volumeConfigs map[string]*config.VolumeConfig, services *config.ServiceConfigs, volumeEnabled bool) (*Volumes, error) {
var err error
volumes := make([]*Volume, 0, len(volumeConfigs))
for name, config := range volumeConfigs {
volume := NewVolume(projectName, name, config, cli)
volumes = append(volumes, volume)
}
return &Volumes{
volumes: volumes,
volumeEnabled: volumeEnabled,
}, err
}
// DockerFactory implements project.VolumesFactory
type DockerFactory struct {
ClientFactory composeclient.Factory
}
// Create implements project.VolumesFactory Create method.
// It creates a Volumes (that implements project.Volumes) from specified configurations.
func (f *DockerFactory) Create(projectName string, volumeConfigs map[string]*config.VolumeConfig, serviceConfigs *config.ServiceConfigs, volumeEnabled bool) (project.Volumes, error) {
cli := f.ClientFactory.Create(nil)
return VolumesFromServices(cli, projectName, volumeConfigs, serviceConfigs, volumeEnabled)
}