github.com/docker/compose-on-kubernetes@v0.5.0/internal/convert/volumes.go (about)

     1  package convert
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/docker/compose-on-kubernetes/api/compose/latest"
    10  	"github.com/pkg/errors"
    11  	apiv1 "k8s.io/api/core/v1"
    12  )
    13  
    14  const dockerSock = "/var/run/docker.sock"
    15  
    16  type volumeSpec struct {
    17  	mount  apiv1.VolumeMount
    18  	source *apiv1.VolumeSource
    19  }
    20  
    21  func hasPersistentVolumes(s latest.ServiceConfig) bool {
    22  	for _, volume := range s.Volumes {
    23  		if volume.Type == "volume" {
    24  			return true
    25  		}
    26  	}
    27  
    28  	return false
    29  }
    30  
    31  func toVolumeSpecs(s latest.ServiceConfig, configuration *latest.StackSpec) ([]volumeSpec, error) {
    32  	var specs []volumeSpec
    33  	for i, m := range s.Volumes {
    34  		var source *apiv1.VolumeSource
    35  		name := fmt.Sprintf("mount-%d", i)
    36  		subpath := ""
    37  		if m.Source == dockerSock && m.Target == dockerSock {
    38  			subpath = "docker.sock"
    39  			source = hostPathVolume("/var/run")
    40  		} else if strings.HasSuffix(m.Source, ".git") {
    41  			source = gitVolume(m.Source)
    42  		} else if m.Type == "volume" {
    43  			if m.Source != "" {
    44  				name = m.Source
    45  			}
    46  		} else {
    47  			// bind mount
    48  			if !filepath.IsAbs(m.Source) {
    49  				return nil, errors.Errorf("%s: only absolute paths can be specified in mount source", m.Source)
    50  			}
    51  			if m.Source == "/" {
    52  				source = hostPathVolume("/")
    53  			} else {
    54  				parent, file := filepath.Split(m.Source)
    55  				if parent != "/" {
    56  					parent = strings.TrimSuffix(parent, "/")
    57  				}
    58  				source = hostPathVolume(parent)
    59  				subpath = file
    60  			}
    61  		}
    62  
    63  		specs = append(specs, volumeSpec{
    64  			source: source,
    65  			mount:  volumeMount(name, m.Target, m.ReadOnly, subpath),
    66  		})
    67  	}
    68  
    69  	for i, m := range s.Tmpfs {
    70  		name := fmt.Sprintf("tmp-%d", i)
    71  
    72  		specs = append(specs, volumeSpec{
    73  			source: emptyVolumeInMemory(),
    74  			mount:  volumeMount(name, m, false, ""),
    75  		})
    76  	}
    77  
    78  	for i, s := range s.Secrets {
    79  		name := fmt.Sprintf("secret-%d", i)
    80  
    81  		target := path.Join("/run/secrets", or(s.Target, s.Source))
    82  		subPath := name
    83  		readOnly := true
    84  
    85  		specs = append(specs, volumeSpec{
    86  			source: secretVolume(s, configuration.Secrets[s.Source], subPath),
    87  			mount:  volumeMount(name, target, readOnly, subPath),
    88  		})
    89  	}
    90  
    91  	for i, c := range s.Configs {
    92  		name := fmt.Sprintf("config-%d", i)
    93  
    94  		target := or(c.Target, "/"+c.Source)
    95  		subPath := name
    96  		readOnly := true
    97  
    98  		specs = append(specs, volumeSpec{
    99  			source: configVolume(c, configuration.Configs[c.Source], subPath),
   100  			mount:  volumeMount(name, target, readOnly, subPath),
   101  		})
   102  	}
   103  
   104  	return specs, nil
   105  }
   106  
   107  func or(v string, defaultValue string) string {
   108  	if v != "" && v != "." {
   109  		return v
   110  	}
   111  
   112  	return defaultValue
   113  }
   114  
   115  func toVolumeMounts(s latest.ServiceConfig, configuration *latest.StackSpec) ([]apiv1.VolumeMount, error) {
   116  	var mounts []apiv1.VolumeMount
   117  	specs, err := toVolumeSpecs(s, configuration)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	for _, spec := range specs {
   122  		mounts = append(mounts, spec.mount)
   123  	}
   124  	return mounts, nil
   125  }
   126  
   127  func toVolumes(s latest.ServiceConfig, configuration *latest.StackSpec) ([]apiv1.Volume, error) {
   128  	var volumes []apiv1.Volume
   129  	specs, err := toVolumeSpecs(s, configuration)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	for _, spec := range specs {
   134  		if spec.source == nil {
   135  			continue
   136  		}
   137  		volumes = append(volumes, apiv1.Volume{
   138  			Name:         spec.mount.Name,
   139  			VolumeSource: *spec.source,
   140  		})
   141  	}
   142  	return volumes, nil
   143  }
   144  
   145  func gitVolume(path string) *apiv1.VolumeSource {
   146  	return &apiv1.VolumeSource{
   147  		GitRepo: &apiv1.GitRepoVolumeSource{
   148  			Repository: filepath.ToSlash(path),
   149  		},
   150  	}
   151  }
   152  
   153  func hostPathVolume(path string) *apiv1.VolumeSource {
   154  	return &apiv1.VolumeSource{
   155  		HostPath: &apiv1.HostPathVolumeSource{
   156  			Path: path,
   157  		},
   158  	}
   159  }
   160  
   161  func defaultMode(mode *uint32) *int32 {
   162  	var defaultMode *int32
   163  
   164  	if mode != nil {
   165  		signedMode := int32(*mode)
   166  		defaultMode = &signedMode
   167  	}
   168  
   169  	return defaultMode
   170  }
   171  
   172  func secretVolume(config latest.ServiceSecretConfig, topLevelSecret latest.SecretConfig, subPath string) *apiv1.VolumeSource {
   173  	return &apiv1.VolumeSource{
   174  		Secret: &apiv1.SecretVolumeSource{
   175  			SecretName: config.Source,
   176  			Items: []apiv1.KeyToPath{
   177  				{
   178  					Key:  toKey(topLevelSecret.File),
   179  					Path: subPath,
   180  					Mode: defaultMode(config.Mode),
   181  				},
   182  			},
   183  		},
   184  	}
   185  }
   186  
   187  func volumeMount(name, path string, readOnly bool, subPath string) apiv1.VolumeMount {
   188  	return apiv1.VolumeMount{
   189  		Name:      name,
   190  		MountPath: path,
   191  		ReadOnly:  readOnly,
   192  		SubPath:   subPath,
   193  	}
   194  }
   195  
   196  func configVolume(config latest.ServiceConfigObjConfig, topLevelConfig latest.ConfigObjConfig, subPath string) *apiv1.VolumeSource {
   197  	return &apiv1.VolumeSource{
   198  		ConfigMap: &apiv1.ConfigMapVolumeSource{
   199  			LocalObjectReference: apiv1.LocalObjectReference{
   200  				Name: config.Source,
   201  			},
   202  			Items: []apiv1.KeyToPath{
   203  				{
   204  					Key:  toKey(topLevelConfig.File),
   205  					Path: subPath,
   206  					Mode: defaultMode(config.Mode),
   207  				},
   208  			},
   209  		},
   210  	}
   211  }
   212  
   213  func toKey(file string) string {
   214  	if file != "" {
   215  		return path.Base(file)
   216  	}
   217  
   218  	return "file" // TODO: hard-coded key for external configs
   219  }
   220  
   221  func emptyVolumeInMemory() *apiv1.VolumeSource {
   222  	return &apiv1.VolumeSource{
   223  		EmptyDir: &apiv1.EmptyDirVolumeSource{
   224  			Medium: apiv1.StorageMediumMemory,
   225  		},
   226  	}
   227  }