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  }