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  }