github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/cli/compose/convert/volume.go (about)

     1  package convert
     2  
     3  import (
     4  	"strings"
     5  
     6  	composetypes "github.com/docker/cli/cli/compose/types"
     7  	"github.com/docker/docker/api/types/mount"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  type volumes map[string]composetypes.VolumeConfig
    12  
    13  // Volumes from compose-file types to engine api types
    14  func Volumes(serviceVolumes []composetypes.ServiceVolumeConfig, stackVolumes volumes, namespace Namespace) ([]mount.Mount, error) {
    15  	var mounts []mount.Mount
    16  
    17  	for _, volumeConfig := range serviceVolumes {
    18  		mount, err := convertVolumeToMount(volumeConfig, stackVolumes, namespace)
    19  		if err != nil {
    20  			return nil, err
    21  		}
    22  		mounts = append(mounts, mount)
    23  	}
    24  	return mounts, nil
    25  }
    26  
    27  func createMountFromVolume(volume composetypes.ServiceVolumeConfig) mount.Mount {
    28  	return mount.Mount{
    29  		Type:        mount.Type(volume.Type),
    30  		Target:      volume.Target,
    31  		ReadOnly:    volume.ReadOnly,
    32  		Source:      volume.Source,
    33  		Consistency: mount.Consistency(volume.Consistency),
    34  	}
    35  }
    36  
    37  func handleVolumeToMount(
    38  	volume composetypes.ServiceVolumeConfig,
    39  	stackVolumes volumes,
    40  	namespace Namespace,
    41  ) (mount.Mount, error) {
    42  	result := createMountFromVolume(volume)
    43  
    44  	if volume.Tmpfs != nil {
    45  		return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume")
    46  	}
    47  	if volume.Bind != nil {
    48  		return mount.Mount{}, errors.New("bind options are incompatible with type volume")
    49  	}
    50  	if volume.Cluster != nil {
    51  		return mount.Mount{}, errors.New("cluster options are incompatible with type volume")
    52  	}
    53  	// Anonymous volumes
    54  	if volume.Source == "" {
    55  		return result, nil
    56  	}
    57  
    58  	stackVolume, exists := stackVolumes[volume.Source]
    59  	if !exists {
    60  		return mount.Mount{}, errors.Errorf("undefined volume %q", volume.Source)
    61  	}
    62  
    63  	result.Source = namespace.Scope(volume.Source)
    64  	result.VolumeOptions = &mount.VolumeOptions{}
    65  
    66  	if volume.Volume != nil {
    67  		result.VolumeOptions.NoCopy = volume.Volume.NoCopy
    68  	}
    69  
    70  	if stackVolume.Name != "" {
    71  		result.Source = stackVolume.Name
    72  	}
    73  
    74  	// External named volumes
    75  	if stackVolume.External.External {
    76  		return result, nil
    77  	}
    78  
    79  	result.VolumeOptions.Labels = AddStackLabel(namespace, stackVolume.Labels)
    80  	if stackVolume.Driver != "" || stackVolume.DriverOpts != nil {
    81  		result.VolumeOptions.DriverConfig = &mount.Driver{
    82  			Name:    stackVolume.Driver,
    83  			Options: stackVolume.DriverOpts,
    84  		}
    85  	}
    86  
    87  	return result, nil
    88  }
    89  
    90  func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
    91  	result := createMountFromVolume(volume)
    92  
    93  	if volume.Source == "" {
    94  		return mount.Mount{}, errors.New("invalid bind source, source cannot be empty")
    95  	}
    96  	if volume.Volume != nil {
    97  		return mount.Mount{}, errors.New("volume options are incompatible with type bind")
    98  	}
    99  	if volume.Tmpfs != nil {
   100  		return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
   101  	}
   102  	if volume.Cluster != nil {
   103  		return mount.Mount{}, errors.New("cluster options are incompatible with type bind")
   104  	}
   105  	if volume.Bind != nil {
   106  		result.BindOptions = &mount.BindOptions{
   107  			Propagation: mount.Propagation(volume.Bind.Propagation),
   108  		}
   109  	}
   110  	return result, nil
   111  }
   112  
   113  func handleTmpfsToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
   114  	result := createMountFromVolume(volume)
   115  
   116  	if volume.Source != "" {
   117  		return mount.Mount{}, errors.New("invalid tmpfs source, source must be empty")
   118  	}
   119  	if volume.Bind != nil {
   120  		return mount.Mount{}, errors.New("bind options are incompatible with type tmpfs")
   121  	}
   122  	if volume.Volume != nil {
   123  		return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs")
   124  	}
   125  	if volume.Cluster != nil {
   126  		return mount.Mount{}, errors.New("cluster options are incompatible with type tmpfs")
   127  	}
   128  	if volume.Tmpfs != nil {
   129  		result.TmpfsOptions = &mount.TmpfsOptions{
   130  			SizeBytes: volume.Tmpfs.Size,
   131  		}
   132  	}
   133  	return result, nil
   134  }
   135  
   136  func handleNpipeToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
   137  	result := createMountFromVolume(volume)
   138  
   139  	if volume.Source == "" {
   140  		return mount.Mount{}, errors.New("invalid npipe source, source cannot be empty")
   141  	}
   142  	if volume.Volume != nil {
   143  		return mount.Mount{}, errors.New("volume options are incompatible with type npipe")
   144  	}
   145  	if volume.Tmpfs != nil {
   146  		return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe")
   147  	}
   148  	if volume.Bind != nil {
   149  		result.BindOptions = &mount.BindOptions{
   150  			Propagation: mount.Propagation(volume.Bind.Propagation),
   151  		}
   152  	}
   153  	return result, nil
   154  }
   155  
   156  func handleClusterToMount(
   157  	volume composetypes.ServiceVolumeConfig,
   158  	stackVolumes volumes,
   159  	namespace Namespace,
   160  ) (mount.Mount, error) {
   161  	if volume.Source == "" {
   162  		return mount.Mount{}, errors.New("invalid cluster source, source cannot be empty")
   163  	}
   164  	if volume.Tmpfs != nil {
   165  		return mount.Mount{}, errors.New("tmpfs options are incompatible with type cluster")
   166  	}
   167  	if volume.Bind != nil {
   168  		return mount.Mount{}, errors.New("bind options are incompatible with type cluster")
   169  	}
   170  	if volume.Volume != nil {
   171  		return mount.Mount{}, errors.New("volume options are incompatible with type cluster")
   172  	}
   173  
   174  	result := createMountFromVolume(volume)
   175  	result.ClusterOptions = &mount.ClusterOptions{}
   176  
   177  	if !strings.HasPrefix(volume.Source, "group:") {
   178  		// if the volume is a cluster volume and the source is a volumegroup, we
   179  		// will ignore checking to see if such a volume is defined. the volume
   180  		// group isn't namespaced, and there's no simple way to indicate that
   181  		// external volumes with a given group exist.
   182  		stackVolume, exists := stackVolumes[volume.Source]
   183  		if !exists {
   184  			return mount.Mount{}, errors.Errorf("undefined volume %q", volume.Source)
   185  		}
   186  
   187  		// if the volume is not specified with a group source, we may namespace
   188  		// the name, if one is not otherwise specified.
   189  		if stackVolume.Name != "" {
   190  			result.Source = stackVolume.Name
   191  		} else {
   192  			result.Source = namespace.Scope(volume.Source)
   193  		}
   194  	}
   195  
   196  	return result, nil
   197  }
   198  
   199  func convertVolumeToMount(
   200  	volume composetypes.ServiceVolumeConfig,
   201  	stackVolumes volumes,
   202  	namespace Namespace,
   203  ) (mount.Mount, error) {
   204  	switch volume.Type {
   205  	case "volume", "":
   206  		return handleVolumeToMount(volume, stackVolumes, namespace)
   207  	case "bind":
   208  		return handleBindToMount(volume)
   209  	case "tmpfs":
   210  		return handleTmpfsToMount(volume)
   211  	case "npipe":
   212  		return handleNpipeToMount(volume)
   213  	case "cluster":
   214  		return handleClusterToMount(volume, stackVolumes, namespace)
   215  	}
   216  	return mount.Mount{}, errors.New("volume type must be volume, bind, tmpfs, npipe, or cluster")
   217  }