github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/compose/convert/volume.go (about)

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