Skip to content

Commit 74c6802

Browse files
committed
Migrate vol funcs from odo to devfile/library
Signed-off-by: Maysun J Faisal <maysunaneek@gmail.com>
1 parent ffe3a0a commit 74c6802

4 files changed

Lines changed: 468 additions & 3 deletions

File tree

pkg/devfile/generator/generators.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,45 @@ func GetImageStream(imageStreamParams ImageStreamParams) imagev1.ImageStream {
284284
}
285285
return imageStream
286286
}
287+
288+
// VolumeInfo is a struct to hold the pvc name and the volume name to create a volume.
289+
type VolumeInfo struct {
290+
PVCName string
291+
VolumeName string
292+
}
293+
294+
// VolumeParams is a struct that contains the required data to create Kubernetes Volumes and mount Volumes in Containers
295+
type VolumeParams struct {
296+
// Containers is a list of containers that needs to be updated for the volume mounts
297+
Containers []corev1.Container
298+
299+
// VolumeNameToVolumeInfo is a map of the devfile volume name to the volume info containing the pvc name and the volume name.
300+
VolumeNameToVolumeInfo map[string]VolumeInfo
301+
}
302+
303+
// GetVolumesAndVolumeMounts gets the PVC volumes and updates the containers with the volume mounts.
304+
func GetVolumesAndVolumeMounts(devfileObj parser.DevfileObj, volumeParams VolumeParams, options common.DevfileOptions) ([]corev1.Volume, error) {
305+
306+
containerComponents, err := devfileObj.Data.GetDevfileContainerComponents(options)
307+
if err != nil {
308+
return nil, err
309+
}
310+
311+
var pvcVols []corev1.Volume
312+
for volName, volInfo := range volumeParams.VolumeNameToVolumeInfo {
313+
pvcVols = append(pvcVols, getPVC(volInfo.VolumeName, volInfo.PVCName))
314+
315+
// containerNameToMountPaths is a map of the Devfile container name to their Devfile Volume Mount Paths for a given Volume Name
316+
containerNameToMountPaths := make(map[string][]string)
317+
for _, containerComp := range containerComponents {
318+
for _, volumeMount := range containerComp.Container.VolumeMounts {
319+
if volName == volumeMount.Name {
320+
containerNameToMountPaths[containerComp.Name] = append(containerNameToMountPaths[containerComp.Name], GetVolumeMountPath(volumeMount))
321+
}
322+
}
323+
}
324+
325+
addVolumeMountToContainers(volumeParams.Containers, volInfo.VolumeName, containerNameToMountPaths)
326+
}
327+
return pvcVols, nil
328+
}

pkg/devfile/generator/generators_test.go

Lines changed: 239 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"reflect"
55
"testing"
66

7-
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
87
v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
98
"github.com/devfile/api/v2/pkg/attributes"
109
"github.com/devfile/library/pkg/devfile/parser"
@@ -28,7 +27,7 @@ func TestGetContainers(t *testing.T) {
2827
trueMountSources := true
2928
falseMountSources := false
3029

31-
project := v1alpha2.Project{
30+
project := v1.Project{
3231
ClonePath: "test-project/",
3332
Name: "project0",
3433
ProjectSource: v1.ProjectSource{
@@ -189,7 +188,7 @@ func TestGetContainers(t *testing.T) {
189188
DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{
190189
DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{
191190
Components: tt.containerComponents,
192-
Projects: []v1alpha2.Project{
191+
Projects: []v1.Project{
193192
project,
194193
},
195194
},
@@ -228,3 +227,240 @@ func TestGetContainers(t *testing.T) {
228227
}
229228

230229
}
230+
231+
func TestGetVolumesAndVolumeMounts(t *testing.T) {
232+
233+
type testVolumeMountInfo struct {
234+
mountPath string
235+
volumeName string
236+
}
237+
238+
tests := []struct {
239+
name string
240+
components []v1.Component
241+
volumeNameToVolInfo map[string]VolumeInfo
242+
wantContainerToVol map[string][]testVolumeMountInfo
243+
wantErr bool
244+
}{
245+
{
246+
name: "One volume mounted",
247+
components: []v1.Component{testingutil.GetFakeContainerComponent("comp1"), testingutil.GetFakeContainerComponent("comp2")},
248+
volumeNameToVolInfo: map[string]VolumeInfo{
249+
"myvolume1": {
250+
PVCName: "volume1-pvc",
251+
VolumeName: "volume1-pvc-vol",
252+
},
253+
},
254+
wantContainerToVol: map[string][]testVolumeMountInfo{
255+
"comp1": {
256+
{
257+
mountPath: "/my/volume/mount/path1",
258+
volumeName: "volume1-pvc-vol",
259+
},
260+
},
261+
"comp2": {
262+
{
263+
mountPath: "/my/volume/mount/path1",
264+
volumeName: "volume1-pvc-vol",
265+
},
266+
},
267+
},
268+
wantErr: false,
269+
},
270+
{
271+
name: "One volume mounted at diff locations",
272+
components: []v1.Component{
273+
{
274+
Name: "container1",
275+
ComponentUnion: v1.ComponentUnion{
276+
Container: &v1.ContainerComponent{
277+
Container: v1.Container{
278+
VolumeMounts: []v1.VolumeMount{
279+
{
280+
Name: "volume1",
281+
Path: "/path1",
282+
},
283+
{
284+
Name: "volume1",
285+
Path: "/path2",
286+
},
287+
},
288+
},
289+
},
290+
},
291+
},
292+
},
293+
volumeNameToVolInfo: map[string]VolumeInfo{
294+
"volume1": {
295+
PVCName: "volume1-pvc",
296+
VolumeName: "volume1-pvc-vol",
297+
},
298+
},
299+
wantContainerToVol: map[string][]testVolumeMountInfo{
300+
"container1": {
301+
{
302+
mountPath: "/path1",
303+
volumeName: "volume1-pvc-vol",
304+
},
305+
{
306+
mountPath: "/path2",
307+
volumeName: "volume1-pvc-vol",
308+
},
309+
},
310+
},
311+
wantErr: false,
312+
},
313+
{
314+
name: "One volume mounted at diff container components",
315+
components: []v1.Component{
316+
{
317+
Name: "container1",
318+
ComponentUnion: v1.ComponentUnion{
319+
Container: &v1.ContainerComponent{
320+
Container: v1.Container{
321+
VolumeMounts: []v1.VolumeMount{
322+
{
323+
Name: "volume1",
324+
Path: "/path1",
325+
},
326+
},
327+
},
328+
},
329+
},
330+
},
331+
{
332+
Name: "container2",
333+
ComponentUnion: v1.ComponentUnion{
334+
Container: &v1.ContainerComponent{
335+
Container: v1.Container{
336+
VolumeMounts: []v1.VolumeMount{
337+
{
338+
Name: "volume1",
339+
Path: "/path2",
340+
},
341+
},
342+
},
343+
},
344+
},
345+
},
346+
},
347+
volumeNameToVolInfo: map[string]VolumeInfo{
348+
"volume1": {
349+
PVCName: "volume1-pvc",
350+
VolumeName: "volume1-pvc-vol",
351+
},
352+
},
353+
wantContainerToVol: map[string][]testVolumeMountInfo{
354+
"container1": {
355+
{
356+
mountPath: "/path1",
357+
volumeName: "volume1-pvc-vol",
358+
},
359+
},
360+
"container2": {
361+
{
362+
mountPath: "/path2",
363+
volumeName: "volume1-pvc-vol",
364+
},
365+
},
366+
},
367+
wantErr: false,
368+
},
369+
{
370+
name: "Invalid case",
371+
components: []v1.Component{
372+
{
373+
Name: "container1",
374+
Attributes: attributes.Attributes{}.FromStringMap(map[string]string{
375+
"firstString": "firstStringValue",
376+
}),
377+
ComponentUnion: v1.ComponentUnion{},
378+
},
379+
},
380+
wantErr: true,
381+
},
382+
}
383+
384+
for _, tt := range tests {
385+
t.Run(tt.name, func(t *testing.T) {
386+
387+
devObj := parser.DevfileObj{
388+
Data: &v2.DevfileV2{
389+
Devfile: v1.Devfile{
390+
DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{
391+
DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{
392+
Components: tt.components,
393+
},
394+
},
395+
},
396+
},
397+
}
398+
399+
containers, err := GetContainers(devObj, common.DevfileOptions{})
400+
if err != nil {
401+
t.Errorf("TestGetVolumesAndVolumeMounts error - %v", err)
402+
return
403+
}
404+
405+
var options common.DevfileOptions
406+
if tt.wantErr {
407+
options = common.DevfileOptions{
408+
Filter: map[string]interface{}{
409+
"firstString": "firstStringValue",
410+
},
411+
}
412+
}
413+
414+
volumeParams := VolumeParams{
415+
Containers: containers,
416+
VolumeNameToVolumeInfo: tt.volumeNameToVolInfo,
417+
}
418+
419+
pvcVols, err := GetVolumesAndVolumeMounts(devObj, volumeParams, options)
420+
if !tt.wantErr && err != nil {
421+
t.Errorf("TestGetVolumesAndVolumeMounts unexpected error: %v", err)
422+
return
423+
} else if tt.wantErr && err != nil {
424+
return
425+
} else if tt.wantErr && err == nil {
426+
t.Error("TestGetVolumesAndVolumeMounts expected error but got nil")
427+
return
428+
}
429+
430+
// check if the pvc volumes returned are correct
431+
for _, volInfo := range tt.volumeNameToVolInfo {
432+
matched := false
433+
for _, pvcVol := range pvcVols {
434+
if volInfo.VolumeName == pvcVol.Name && pvcVol.PersistentVolumeClaim != nil && volInfo.PVCName == pvcVol.PersistentVolumeClaim.ClaimName {
435+
matched = true
436+
}
437+
}
438+
439+
if !matched {
440+
t.Errorf("TestGetVolumesAndVolumeMounts error - could not find volume details %s in the actual result", volInfo.VolumeName)
441+
}
442+
}
443+
444+
// check the volume mounts of the containers
445+
for _, container := range containers {
446+
if volMounts, ok := tt.wantContainerToVol[container.Name]; !ok {
447+
t.Errorf("TestGetVolumesAndVolumeMounts error - did not find the expected container %s", container.Name)
448+
return
449+
} else {
450+
for _, expectedVolMount := range volMounts {
451+
matched := false
452+
for _, actualVolMount := range container.VolumeMounts {
453+
if expectedVolMount.volumeName == actualVolMount.Name && expectedVolMount.mountPath == actualVolMount.MountPath {
454+
matched = true
455+
}
456+
}
457+
458+
if !matched {
459+
t.Errorf("TestGetVolumesAndVolumeMounts error - could not find volume mount details for path %s in the actual result for container %s", expectedVolMount.mountPath, container.Name)
460+
}
461+
}
462+
}
463+
}
464+
})
465+
}
466+
}

pkg/devfile/generator/utils.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,3 +419,46 @@ func getBuildConfigSpec(buildConfigSpecParams BuildConfigSpecParams) *buildv1.Bu
419419
},
420420
}
421421
}
422+
423+
// GetVolumeMountPath gets the volume mount's path.
424+
func GetVolumeMountPath(volumeMount v1.VolumeMount) string {
425+
// if there is no volume mount path, default to volume mount name as per devfile schema
426+
if volumeMount.Path == "" {
427+
volumeMount.Path = "/" + volumeMount.Name
428+
}
429+
430+
return volumeMount.Path
431+
}
432+
433+
// getPVC gets a pvc type volume with the given volume name and pvc name.
434+
func getPVC(volumeName, pvcName string) corev1.Volume {
435+
436+
return corev1.Volume{
437+
Name: volumeName,
438+
VolumeSource: corev1.VolumeSource{
439+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
440+
ClaimName: pvcName,
441+
},
442+
},
443+
}
444+
}
445+
446+
// addVolumeMountToContainers adds the Volume Mounts in containerNameToMountPaths to the containers for a given volumeName.
447+
// containerNameToMountPaths is a map of a container name to an array of its Mount Paths.
448+
func addVolumeMountToContainers(containers []corev1.Container, volumeName string, containerNameToMountPaths map[string][]string) {
449+
450+
for containerName, mountPaths := range containerNameToMountPaths {
451+
for i := range containers {
452+
if containers[i].Name == containerName {
453+
for _, mountPath := range mountPaths {
454+
containers[i].VolumeMounts = append(containers[i].VolumeMounts, corev1.VolumeMount{
455+
Name: volumeName,
456+
MountPath: mountPath,
457+
SubPath: "",
458+
},
459+
)
460+
}
461+
}
462+
}
463+
}
464+
}

0 commit comments

Comments
 (0)