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 }