1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-11-06 09:09:19 +02:00

Add support for volume mounts (#4673)

* Add support for volume mounts

* Adatpt unit test to include VolumeMounts

Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>

* Only accept volumeMounts with the name volume

---------

Co-authored-by: Johannes Dillmann <j.dillmann@sap.com>
Co-authored-by: Philipp Stehle <philipp.stehle@sap.com>
Co-authored-by: Anil Keshav <anil.keshav@sap.com>
This commit is contained in:
Ralf Pannemans
2023-12-01 13:33:08 +01:00
committed by GitHub
parent aab4de4597
commit 6efb21b30b
4 changed files with 56 additions and 35 deletions

View File

@@ -13,6 +13,8 @@ import (
"github.com/pkg/errors"
)
const SupportedVolumeName = "volume"
// StepData defines the metadata for a step, like step descriptions, parameters, ...
type StepData struct {
Metadata StepMetadata `json:"metadata"`
@@ -105,25 +107,25 @@ type StepOutputs struct {
// Container defines an execution container
type Container struct {
//ToDo: check dockerOptions, dockerVolumeBind, containerPortMappings, sidecarOptions, sidecarVolumeBind
Command []string `json:"command"`
EnvVars []EnvVar `json:"env"`
Image string `json:"image"`
ImagePullPolicy string `json:"imagePullPolicy"`
Name string `json:"name"`
ReadyCommand string `json:"readyCommand"`
Shell string `json:"shell"`
WorkingDir string `json:"workingDir"`
Conditions []Condition `json:"conditions,omitempty"`
Options []Option `json:"options,omitempty"`
//VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
Command []string `json:"command"`
EnvVars []EnvVar `json:"env"`
Image string `json:"image"`
ImagePullPolicy string `json:"imagePullPolicy"`
Name string `json:"name"`
ReadyCommand string `json:"readyCommand"`
Shell string `json:"shell"`
WorkingDir string `json:"workingDir"`
Conditions []Condition `json:"conditions,omitempty"`
Options []Option `json:"options,omitempty"`
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
}
// ToDo: Add the missing Volumes part to enable the volume mount completely
// VolumeMount defines a mount path
// type VolumeMount struct {
// MountPath string `json:"mountPath"`
// Name string `json:"name"`
//}
type VolumeMount struct {
Name string `json:"name"`
MountPath string `json:"mountPath"`
}
// Option defines an docker option
type Option struct {
@@ -385,7 +387,7 @@ func (container *Container) commonConfiguration(keyPrefix string, config *map[st
}
putStringIfNotEmpty(*config, keyPrefix+"Workspace", container.WorkingDir)
putSliceIfNotEmpty(*config, keyPrefix+"Options", OptionsAsStringSlice(container.Options))
//putSliceIfNotEmpty(*config, keyPrefix+"VolumeBind", volumeMountsAsStringSlice(container.VolumeMounts))
putSliceIfNotEmpty(*config, keyPrefix+"VolumeBind", volumeMountsAsStringSlice(container.VolumeMounts))
}
@@ -518,11 +520,14 @@ func ResolveMetadata(gitHubTokens map[string]string, metaDataResolver func() map
return metadata, nil
}
//ToDo: Enable this when the Volumes part is also implemented
//func volumeMountsAsStringSlice(volumeMounts []VolumeMount) []string {
// e := []string{}
// for _, v := range volumeMounts {
// e = append(e, fmt.Sprintf("%v:%v", v.Name, v.MountPath))
// }
// return e
//}
func volumeMountsAsStringSlice(volumeMounts []VolumeMount) []string {
e := []string{}
for _, v := range volumeMounts {
if v.Name != SupportedVolumeName {
log.Entry().Warningf("Unsupported volume name: %q, only %q is supported", v.Name, SupportedVolumeName)
continue
}
e = append(e, fmt.Sprintf("%v:%v", v.Name, v.MountPath))
}
return e
}

View File

@@ -397,10 +397,10 @@ func TestGetContextDefaults(t *testing.T) {
{Name: "opt1", Value: "optValue1"},
{Name: "opt2", Value: "optValue2"},
},
//VolumeMounts: []VolumeMount{
// {MountPath: "mp1", Name: "mn1"},
// {MountPath: "mp2", Name: "mn2"},
//},
VolumeMounts: []VolumeMount{
{MountPath: "mp1", Name: "volume"},
{MountPath: "mp2", Name: "mn2"},
},
},
},
Sidecars: []Container{
@@ -419,10 +419,10 @@ func TestGetContextDefaults(t *testing.T) {
{Name: "opt3", Value: "optValue3"},
{Name: "opt4", Value: "optValue4"},
},
//VolumeMounts: []VolumeMount{
// {MountPath: "mp3", Name: "mn3"},
// {MountPath: "mp4", Name: "mn4"},
//},
VolumeMounts: []VolumeMount{
{MountPath: "mp3", Name: "mn3"},
{MountPath: "mp4", Name: "volume"},
},
},
},
},
@@ -451,7 +451,7 @@ func TestGetContextDefaults(t *testing.T) {
assert.Equal(t, true, d.Defaults[0].Steps["testStep"]["dockerPullImage"], "dockerPullImage default not available")
assert.Equal(t, "/test/dir", d.Defaults[0].Steps["testStep"]["dockerWorkspace"], "dockerWorkspace default not available")
assert.Equal(t, []interface{}{"opt1 optValue1", "opt2 optValue2"}, d.Defaults[0].Steps["testStep"]["dockerOptions"], "dockerOptions default not available")
//assert.Equal(t, []interface{}{"mn1:mp1", "mn2:mp2"}, d.Defaults[0].Steps["testStep"]["dockerVolumeBind"], "dockerVolumeBind default not available")
assert.Equal(t, []interface{}{"volume:mp1"}, d.Defaults[0].Steps["testStep"]["dockerVolumeBind"], "dockerVolumeBind default not available")
assert.Equal(t, "/sidecar/command", d.Defaults[0].Steps["testStep"]["sidecarCommand"], "sidecarCommand default not available")
assert.Equal(t, map[string]interface{}{"env3": "val3", "env4": "val4"}, d.Defaults[0].Steps["testStep"]["sidecarEnvVars"], "sidecarEnvVars default not available")
@@ -461,7 +461,7 @@ func TestGetContextDefaults(t *testing.T) {
assert.Equal(t, "/sidecar/command", d.Defaults[0].Steps["testStep"]["sidecarReadyCommand"], "sidecarReadyCommand default not available")
assert.Equal(t, "/sidecar/dir", d.Defaults[0].Steps["testStep"]["sidecarWorkspace"], "sidecarWorkspace default not available")
assert.Equal(t, []interface{}{"opt3 optValue3", "opt4 optValue4"}, d.Defaults[0].Steps["testStep"]["sidecarOptions"], "sidecarOptions default not available")
//assert.Equal(t, []interface{}{"mn3:mp3", "mn4:mp4"}, d.Defaults[0].Steps["testStep"]["sidecarVolumeBind"], "sidecarVolumeBind default not available")
assert.Equal(t, []interface{}{"volume:mp4"}, d.Defaults[0].Steps["testStep"]["sidecarVolumeBind"], "sidecarVolumeBind default not available")
})
t.Run("Container conditions", func(t *testing.T) {

View File

@@ -183,6 +183,7 @@ void call(Map parameters = [:], body) {
}
def securityContext = securityContextFromOptions(config.dockerOptions)
def containerMountPath = containerMountPathFromVolumeBind(config.dockerVolumeBind)
if (env.POD_NAME && isContainerDefined(config)) {
container(getContainerDefined(config)) {
withEnv(dockerEnvVars) {
@@ -208,6 +209,7 @@ void call(Map parameters = [:], body) {
stashContent: config.stashContent,
stashNoDefaultExcludes: config.stashNoDefaultExcludes,
securityContext: securityContext,
containerMountPath: containerMountPath,
]
if (config.sidecarImage) {
@@ -379,6 +381,17 @@ def securityContextFromOptions(dockerOptions) {
return securityContext
}
/*
* Picks the first volumeBind option and translates it into containerMountPath, currently only one fix volume is supported
*/
@NonCPS
def containerMountPathFromVolumeBind(dockerVolumeBind) {
if (dockerVolumeBind) {
return dockerVolumeBind[0].split(":")[1]
}
return ""
}
boolean isContainerDefined(config) {
Map containerMap = ContainerMap.instance.getMap()

View File

@@ -582,9 +582,12 @@ private List getContainerList(config) {
command : []
]
def resources = getResources(sideCarContainerName, config)
if(resources) {
if (resources) {
containerSpec.resources = resources
}
if (config.containerMountPath) {
containerSpec.volumeMounts = [[name: "volume", mountPath: config.containerMountPath]]
}
result.push(containerSpec)
}
return result