1 package storage
2
3 import (
4 "fmt"
5 "path/filepath"
6 "sort"
7 "strings"
8
9 "github.com/devfile/library/v2/pkg/devfile/generator"
10 devfileParser "github.com/devfile/library/v2/pkg/devfile/parser"
11 parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
12 dfutil "github.com/devfile/library/v2/pkg/util"
13
14 "github.com/redhat-developer/odo/pkg/configAutomount"
15 "github.com/redhat-developer/odo/pkg/kclient"
16 odolabels "github.com/redhat-developer/odo/pkg/labels"
17 "github.com/redhat-developer/odo/pkg/storage"
18
19 corev1 "k8s.io/api/core/v1"
20 kerrors "k8s.io/apimachinery/pkg/api/errors"
21 "k8s.io/apimachinery/pkg/api/resource"
22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23 )
24
25
26
27 type VolumeInfo struct {
28 PVCName string
29 VolumeName string
30 }
31
32
33 func GetVolumeInfos(pvcs []corev1.PersistentVolumeClaim) (odoSourcePVCName string, infos map[string]VolumeInfo, _ error) {
34 infos = make(map[string]VolumeInfo)
35 for _, pvc := range pvcs {
36
37 if pvc.DeletionTimestamp != nil {
38 continue
39 }
40
41 generatedVolumeName, e := generateVolumeNameFromPVC(pvc.Name)
42 if e != nil {
43 return "", nil, fmt.Errorf("unable to generate volume name from pvc name: %w", e)
44 }
45
46 storageName := odolabels.GetStorageName(pvc.Labels)
47 if storageName == storage.OdoSourceVolume {
48 odoSourcePVCName = pvc.Name
49 continue
50 }
51
52 infos[storageName] = VolumeInfo{
53 PVCName: pvc.Name,
54 VolumeName: generatedVolumeName,
55 }
56 }
57 return odoSourcePVCName, infos, nil
58 }
59
60
61
62
63 func GetPersistentVolumesAndVolumeMounts(devfileObj devfileParser.DevfileObj, containers []corev1.Container, initContainers []corev1.Container, volumeNameToVolInfo map[string]VolumeInfo, options parsercommon.DevfileOptions) ([]corev1.Volume, error) {
64
65 containerComponents, err := devfileObj.Data.GetDevfileContainerComponents(options)
66 if err != nil {
67 return nil, err
68 }
69
70 var pvcVols []corev1.Volume
71
72
73 keys := make([]string, 0, len(volumeNameToVolInfo))
74 for k := range volumeNameToVolInfo {
75 keys = append(keys, k)
76 }
77 sort.Strings(keys)
78
79 for _, volName := range keys {
80 volInfo := volumeNameToVolInfo[volName]
81 pvcVols = append(pvcVols, getPVC(volInfo.VolumeName, volInfo.PVCName))
82
83
84 containerNameToMountPaths := make(map[string][]string)
85 for _, containerComp := range containerComponents {
86 for _, volumeMount := range containerComp.Container.VolumeMounts {
87 if volName == volumeMount.Name {
88 containerNameToMountPaths[containerComp.Name] = append(containerNameToMountPaths[containerComp.Name], generator.GetVolumeMountPath(volumeMount))
89 }
90 }
91 }
92
93 addVolumeMountToContainers(containers, initContainers, volInfo.VolumeName, containerNameToMountPaths)
94 }
95 return pvcVols, nil
96 }
97
98 func GetEphemeralVolumesAndVolumeMounts(devfileObj devfileParser.DevfileObj, containers []corev1.Container, initContainers []corev1.Container, ephemerals map[string]storage.Storage, options parsercommon.DevfileOptions) ([]corev1.Volume, error) {
99 containerComponents, err := devfileObj.Data.GetDevfileContainerComponents(options)
100 if err != nil {
101 return nil, err
102 }
103 var emptydirVols []corev1.Volume
104
105
106 keys := make([]string, 0, len(ephemerals))
107 for k := range ephemerals {
108 keys = append(keys, k)
109 }
110 sort.Strings(keys)
111
112 for _, volName := range keys {
113 volInfo := ephemerals[volName]
114 emptyDir, err := getEmptyDir(volInfo.Name, volInfo.Spec.Size)
115 if err != nil {
116 return nil, err
117 }
118 emptydirVols = append(emptydirVols, emptyDir)
119
120
121 containerNameToMountPaths := make(map[string][]string)
122 for _, containerComp := range containerComponents {
123 for _, volumeMount := range containerComp.Container.VolumeMounts {
124 if volName == volumeMount.Name {
125 containerNameToMountPaths[containerComp.Name] = append(containerNameToMountPaths[containerComp.Name], generator.GetVolumeMountPath(volumeMount))
126 }
127 }
128 }
129
130 addVolumeMountToContainers(containers, initContainers, volInfo.Name, containerNameToMountPaths)
131 }
132 return emptydirVols, nil
133 }
134
135
136 func getPVC(volumeName, pvcName string) corev1.Volume {
137
138 return corev1.Volume{
139 Name: volumeName,
140 VolumeSource: corev1.VolumeSource{
141 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
142 ClaimName: pvcName,
143 },
144 },
145 }
146 }
147
148
149
150 func getEmptyDir(volumeName string, size string) (corev1.Volume, error) {
151
152 emptyDir := &corev1.EmptyDirVolumeSource{}
153 qty, err := resource.ParseQuantity(size)
154 if err != nil {
155 return corev1.Volume{}, err
156 }
157 emptyDir.SizeLimit = &qty
158 return corev1.Volume{
159 Name: volumeName,
160 VolumeSource: corev1.VolumeSource{
161 EmptyDir: emptyDir,
162 },
163 }, nil
164 }
165
166
167
168
169 func addVolumeMountToContainers(containers []corev1.Container, initContainers []corev1.Container, volumeName string, containerNameToMountPaths map[string][]string) {
170
171 for containerName, mountPaths := range containerNameToMountPaths {
172 for i := range containers {
173 if containers[i].Name == containerName {
174 for _, mountPath := range mountPaths {
175 containers[i].VolumeMounts = append(containers[i].VolumeMounts, corev1.VolumeMount{
176 Name: volumeName,
177 MountPath: mountPath,
178 SubPath: "",
179 },
180 )
181 }
182 }
183 }
184 for i := range initContainers {
185 if strings.HasPrefix(initContainers[i].Name, containerName) {
186 for _, mountPath := range mountPaths {
187 initContainers[i].VolumeMounts = append(initContainers[i].VolumeMounts, corev1.VolumeMount{
188 Name: volumeName,
189 MountPath: mountPath,
190 SubPath: "",
191 },
192 )
193 }
194 }
195 }
196 }
197 }
198
199
200 func generateVolumeNameFromPVC(pvc string) (volumeName string, err error) {
201 volumeName, err = dfutil.NamespaceOpenShiftObject(pvc, "vol")
202 if err != nil {
203 return "", err
204 }
205 return
206 }
207
208
209
210
211 func HandleOdoSourceStorage(client kclient.ClientInterface, storageClient storage.Client, componentName string, isEphemeral bool) error {
212 selector := odolabels.Builder().WithComponentName(componentName).WithSourcePVC(storage.OdoSourceVolume).Selector()
213 pvcs, err := client.ListPVCs(selector)
214 if err != nil && !kerrors.IsNotFound(err) {
215 return err
216 }
217
218 if !isEphemeral {
219 if len(pvcs) == 0 {
220 err := storageClient.Create(storage.Storage{
221 ObjectMeta: metav1.ObjectMeta{
222 Name: storage.OdoSourceVolume,
223 },
224 Spec: storage.StorageSpec{
225 Size: storage.OdoSourceVolumeSize,
226 },
227 })
228
229 if err != nil {
230 return err
231 }
232 } else if len(pvcs) > 1 {
233 return fmt.Errorf("number of source volumes shouldn't be greater than 1")
234 }
235 } else {
236 if len(pvcs) > 0 {
237 for _, pvc := range pvcs {
238 err := client.DeletePVC(pvc.Name)
239 if err != nil {
240 return err
241 }
242 }
243 }
244 }
245 return nil
246 }
247
248 func GetAutomountVolumes(configAutomountClient configAutomount.Client, containers, initContainers []corev1.Container) ([]corev1.Volume, error) {
249 volumesInfos, err := configAutomountClient.GetAutomountingVolumes()
250 if err != nil {
251 return nil, err
252 }
253
254 var volumes []corev1.Volume
255 for _, volumeInfo := range volumesInfos {
256 switch volumeInfo.VolumeType {
257 case configAutomount.VolumeTypePVC:
258 volumes = mountPVC(volumeInfo, containers, initContainers, volumes)
259 case configAutomount.VolumeTypeSecret:
260 volumes = mountSecret(volumeInfo, containers, initContainers, volumes)
261 case configAutomount.VolumeTypeConfigmap:
262 volumes = mountConfigMap(volumeInfo, containers, initContainers, volumes)
263 }
264 }
265 return volumes, nil
266 }
267
268 func mountPVC(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
269 volumeName := "auto-pvc-" + volumeInfo.VolumeName
270
271 inAllContainers(containers, initContainers, func(container *corev1.Container) {
272 addVolumeMountToContainer(container, corev1.VolumeMount{
273 Name: volumeName,
274 MountPath: volumeInfo.MountPath,
275 ReadOnly: volumeInfo.ReadOnly,
276 })
277 })
278
279 volumes = append(volumes, corev1.Volume{
280 Name: volumeName,
281 VolumeSource: corev1.VolumeSource{
282 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
283 ClaimName: volumeInfo.VolumeName,
284 },
285 },
286 })
287 return volumes
288 }
289
290 func mountSecret(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
291 switch volumeInfo.MountAs {
292 case configAutomount.MountAsFile:
293 return mountSecretAsFile(volumeInfo, containers, initContainers, volumes)
294 case configAutomount.MountAsEnv:
295 return mountSecretAsEnv(volumeInfo, containers, initContainers, volumes)
296 case configAutomount.MountAsSubpath:
297 return mountSecretAsSubpath(volumeInfo, containers, initContainers, volumes)
298 }
299 return volumes
300 }
301
302 func mountSecretAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
303 volumeName := "auto-secret-" + volumeInfo.VolumeName
304
305 inAllContainers(containers, initContainers, func(container *corev1.Container) {
306 addVolumeMountToContainer(container, corev1.VolumeMount{
307 Name: volumeName,
308 MountPath: volumeInfo.MountPath,
309 ReadOnly: volumeInfo.ReadOnly,
310 })
311 })
312
313 volumes = append(volumes, corev1.Volume{
314 Name: volumeName,
315 VolumeSource: corev1.VolumeSource{
316 Secret: &corev1.SecretVolumeSource{
317 SecretName: volumeInfo.VolumeName,
318 DefaultMode: volumeInfo.MountAccessMode,
319 },
320 },
321 })
322 return volumes
323 }
324
325 func mountSecretAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
326 inAllContainers(containers, initContainers, func(container *corev1.Container) {
327 addEnvFromToContainer(container, corev1.EnvFromSource{
328 SecretRef: &corev1.SecretEnvSource{
329 LocalObjectReference: corev1.LocalObjectReference{
330 Name: volumeInfo.VolumeName,
331 },
332 },
333 })
334 })
335 return volumes
336 }
337
338 func mountSecretAsSubpath(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
339 volumeName := "auto-secret-" + volumeInfo.VolumeName
340
341 inAllContainers(containers, initContainers, func(container *corev1.Container) {
342 for _, key := range volumeInfo.Keys {
343 addVolumeMountToContainer(container, corev1.VolumeMount{
344 Name: volumeName,
345 MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)),
346 SubPath: key,
347 ReadOnly: volumeInfo.ReadOnly,
348 })
349 }
350 })
351
352 volumes = append(volumes, corev1.Volume{
353 Name: volumeName,
354 VolumeSource: corev1.VolumeSource{
355 Secret: &corev1.SecretVolumeSource{
356 SecretName: volumeInfo.VolumeName,
357 DefaultMode: volumeInfo.MountAccessMode,
358 },
359 },
360 })
361 return volumes
362 }
363
364 func mountConfigMap(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
365 switch volumeInfo.MountAs {
366 case configAutomount.MountAsFile:
367 return mountConfigMapAsFile(volumeInfo, containers, initContainers, volumes)
368 case configAutomount.MountAsEnv:
369 return mountConfigMapAsEnv(volumeInfo, containers, initContainers, volumes)
370 case configAutomount.MountAsSubpath:
371 return mountConfigMapAsSubpath(volumeInfo, containers, initContainers, volumes)
372 }
373 return volumes
374 }
375
376 func mountConfigMapAsFile(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
377 volumeName := "auto-cm-" + volumeInfo.VolumeName
378
379 inAllContainers(containers, initContainers, func(container *corev1.Container) {
380 addVolumeMountToContainer(container, corev1.VolumeMount{
381 Name: volumeName,
382 MountPath: volumeInfo.MountPath,
383 ReadOnly: volumeInfo.ReadOnly,
384 })
385 })
386
387 volumes = append(volumes, corev1.Volume{
388 Name: volumeName,
389 VolumeSource: corev1.VolumeSource{
390 ConfigMap: &corev1.ConfigMapVolumeSource{
391 DefaultMode: volumeInfo.MountAccessMode,
392 LocalObjectReference: corev1.LocalObjectReference{
393 Name: volumeInfo.VolumeName,
394 },
395 },
396 },
397 })
398 return volumes
399 }
400
401 func mountConfigMapAsEnv(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
402 inAllContainers(containers, initContainers, func(container *corev1.Container) {
403 addEnvFromToContainer(container, corev1.EnvFromSource{
404 ConfigMapRef: &corev1.ConfigMapEnvSource{
405 LocalObjectReference: corev1.LocalObjectReference{
406 Name: volumeInfo.VolumeName,
407 },
408 },
409 })
410 })
411 return volumes
412 }
413
414 func mountConfigMapAsSubpath(volumeInfo configAutomount.AutomountInfo, containers, initContainers []corev1.Container, volumes []corev1.Volume) []corev1.Volume {
415 volumeName := "auto-cm-" + volumeInfo.VolumeName
416
417 inAllContainers(containers, initContainers, func(container *corev1.Container) {
418 for _, key := range volumeInfo.Keys {
419 addVolumeMountToContainer(container, corev1.VolumeMount{
420 Name: volumeName,
421 MountPath: filepath.ToSlash(filepath.Join(volumeInfo.MountPath, key)),
422 SubPath: key,
423 ReadOnly: volumeInfo.ReadOnly,
424 })
425 }
426 })
427
428 volumes = append(volumes, corev1.Volume{
429 Name: volumeName,
430 VolumeSource: corev1.VolumeSource{
431 ConfigMap: &corev1.ConfigMapVolumeSource{
432 LocalObjectReference: corev1.LocalObjectReference{
433 Name: volumeInfo.VolumeName,
434 },
435 DefaultMode: volumeInfo.MountAccessMode,
436 },
437 },
438 })
439 return volumes
440 }
441
442 func inAllContainers(containers, initContainers []corev1.Container, f func(container *corev1.Container)) {
443 for i := range containers {
444 f(&containers[i])
445 }
446 for i := range initContainers {
447 f(&initContainers[i])
448 }
449 }
450
451 func addVolumeMountToContainer(container *corev1.Container, volumeMount corev1.VolumeMount) {
452 container.VolumeMounts = append(container.VolumeMounts, volumeMount)
453 }
454
455 func addEnvFromToContainer(container *corev1.Container, envFrom corev1.EnvFromSource) {
456 container.EnvFrom = append(container.EnvFrom, envFrom)
457 }
458
View as plain text