Skip to content

Commit a4c8aaa

Browse files
committed
Add test cases for merging automount volumes to projected volumes
Signed-off-by: Angel Misevski <amisevsk@redhat.com>
1 parent ed320b4 commit a4c8aaa

1 file changed

Lines changed: 332 additions & 0 deletions

File tree

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// Copyright (c) 2019-2023 Red Hat, Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package automount
15+
16+
import (
17+
"path"
18+
"strings"
19+
"testing"
20+
21+
"github.com/devfile/devworkspace-operator/pkg/common"
22+
"github.com/google/go-cmp/cmp"
23+
"github.com/google/go-cmp/cmp/cmpopts"
24+
"github.com/stretchr/testify/assert"
25+
corev1 "k8s.io/api/core/v1"
26+
"k8s.io/utils/pointer"
27+
)
28+
29+
var resourcesDiffOpts = cmp.Options{
30+
cmpopts.SortSlices(func(a, b corev1.Volume) bool {
31+
return strings.Compare(a.Name, b.Name) > 0
32+
}),
33+
cmpopts.SortSlices(func(a, b corev1.VolumeMount) bool {
34+
return strings.Compare(a.Name, b.Name) > 0
35+
}),
36+
cmpopts.SortSlices(func(a, b corev1.EnvFromSource) bool {
37+
var aName, bName string
38+
if a.ConfigMapRef != nil {
39+
aName = a.ConfigMapRef.Name
40+
} else {
41+
aName = a.SecretRef.Name
42+
}
43+
if b.ConfigMapRef != nil {
44+
bName = b.ConfigMapRef.Name
45+
} else {
46+
bName = b.SecretRef.Name
47+
}
48+
49+
return strings.Compare(aName, bName) > 0
50+
}),
51+
}
52+
53+
func TestMergeProjectedVolumes(t *testing.T) {
54+
tests := []struct {
55+
name string
56+
inputResources *Resources
57+
expectedResources *Resources
58+
errRegexp string
59+
}{
60+
{
61+
name: "No merging necessary",
62+
inputResources: &Resources{
63+
Volumes: []corev1.Volume{
64+
testSecretVolume("test-secret"),
65+
testConfigmapVolume("test-configmap"),
66+
testPVCVolume("test-pvc"),
67+
},
68+
VolumeMounts: []corev1.VolumeMount{
69+
testVolumeMount("test-secret", "test-secret"),
70+
testVolumeMount("test-configmap", "test-configmap"),
71+
testVolumeMount("test-pvc", "test-pvc"),
72+
},
73+
EnvFromSource: []corev1.EnvFromSource{
74+
testEnvFromSource("test-envfrom"),
75+
},
76+
},
77+
expectedResources: &Resources{
78+
Volumes: []corev1.Volume{
79+
testSecretVolume("test-secret"),
80+
testConfigmapVolume("test-configmap"),
81+
testPVCVolume("test-pvc"),
82+
},
83+
VolumeMounts: []corev1.VolumeMount{
84+
testVolumeMount("test-secret", "test-secret"),
85+
testVolumeMount("test-configmap", "test-configmap"),
86+
testVolumeMount("test-pvc", "test-pvc"),
87+
},
88+
EnvFromSource: []corev1.EnvFromSource{
89+
testEnvFromSource("test-envfrom"),
90+
},
91+
},
92+
},
93+
{
94+
name: "Merges configmap and secret volume",
95+
inputResources: &Resources{
96+
Volumes: []corev1.Volume{
97+
testSecretVolume("test-secret"),
98+
testConfigmapVolume("test-configmap"),
99+
testPVCVolume("test-pvc"),
100+
},
101+
VolumeMounts: []corev1.VolumeMount{
102+
testVolumeMount("test-secret", "test-path"),
103+
testVolumeMount("test-configmap", "test-path"),
104+
testVolumeMount("test-pvc", "test-pvc"),
105+
},
106+
EnvFromSource: []corev1.EnvFromSource{
107+
testEnvFromSource("test-envfrom"),
108+
},
109+
},
110+
expectedResources: &Resources{
111+
Volumes: []corev1.Volume{
112+
testProjectedVolume(common.AutoMountProjectedVolumeName("test-path"), []string{"test-secret"}, []string{"test-configmap"}),
113+
testPVCVolume("test-pvc"),
114+
},
115+
VolumeMounts: []corev1.VolumeMount{
116+
testVolumeMount(common.AutoMountProjectedVolumeName("test-path"), "test-path"),
117+
testVolumeMount("test-pvc", "test-pvc"),
118+
},
119+
EnvFromSource: []corev1.EnvFromSource{
120+
testEnvFromSource("test-envfrom"),
121+
},
122+
},
123+
},
124+
{
125+
name: "Merges only necessary volumes",
126+
inputResources: &Resources{
127+
Volumes: []corev1.Volume{
128+
testSecretVolume("test-secret"),
129+
testConfigmapVolume("test-configmap"),
130+
testConfigmapVolume("test-configmap-2"),
131+
testSecretVolume("test-unmerged-secret"),
132+
testConfigmapVolume("test-unmerged-configmap"),
133+
testPVCVolume("test-pvc"),
134+
},
135+
VolumeMounts: []corev1.VolumeMount{
136+
testVolumeMount("test-secret", "test-path"),
137+
testVolumeMount("test-configmap", "test-path"),
138+
testVolumeMount("test-configmap-2", "test-path"),
139+
testVolumeMount("test-unmerged-secret", "secret-path"),
140+
testVolumeMount("test-unmerged-configmap", "cm-path"),
141+
testVolumeMount("test-pvc", "test-pvc"),
142+
},
143+
EnvFromSource: []corev1.EnvFromSource{
144+
testEnvFromSource("test-envfrom"),
145+
},
146+
},
147+
expectedResources: &Resources{
148+
Volumes: []corev1.Volume{
149+
testProjectedVolume(common.AutoMountProjectedVolumeName("test-path"), []string{"test-secret"}, []string{"test-configmap", "test-configmap-2"}),
150+
testSecretVolume("test-unmerged-secret"),
151+
testConfigmapVolume("test-unmerged-configmap"),
152+
testPVCVolume("test-pvc"),
153+
},
154+
VolumeMounts: []corev1.VolumeMount{
155+
testVolumeMount(common.AutoMountProjectedVolumeName("test-path"), "test-path"),
156+
testVolumeMount("test-unmerged-secret", "secret-path"),
157+
testVolumeMount("test-unmerged-configmap", "cm-path"),
158+
testVolumeMount("test-pvc", "test-pvc"),
159+
},
160+
EnvFromSource: []corev1.EnvFromSource{
161+
testEnvFromSource("test-envfrom"),
162+
},
163+
},
164+
},
165+
{
166+
name: "Error when would merge subpath volumeMount",
167+
inputResources: &Resources{
168+
Volumes: []corev1.Volume{
169+
testSecretVolume("test-secret"),
170+
testConfigmapVolume("test-configmap"),
171+
},
172+
VolumeMounts: []corev1.VolumeMount{
173+
testVolumeMount("test-secret", "test-path/subpath"),
174+
testSubpathVolumeMount("test-configmap", "test-path", "subpath"),
175+
},
176+
EnvFromSource: []corev1.EnvFromSource{
177+
testEnvFromSource("test-envfrom"),
178+
},
179+
},
180+
errRegexp: `auto-mounted volumes from \(secret 'test-secret', configmap 'test-configmap'\) have the same mount path`,
181+
},
182+
{
183+
name: "Error when unrecognized volume type",
184+
inputResources: &Resources{
185+
Volumes: []corev1.Volume{
186+
{
187+
Name: "unrecognized-volume",
188+
VolumeSource: corev1.VolumeSource{
189+
HostPath: &corev1.HostPathVolumeSource{Path: "test"},
190+
},
191+
},
192+
testConfigmapVolume("test-cm"),
193+
},
194+
VolumeMounts: []corev1.VolumeMount{
195+
testVolumeMount("unrecognized-volume", "test"),
196+
testVolumeMount("test-cm", "test"),
197+
},
198+
},
199+
errRegexp: `unrecognized volume type for volume unrecognized-volume`,
200+
},
201+
{
202+
name: "Error when trying to merge PVC and configmap",
203+
inputResources: &Resources{
204+
Volumes: []corev1.Volume{
205+
testSecretVolume("test-secret"),
206+
testConfigmapVolume("test-configmap"),
207+
testPVCVolume("test-pvc"),
208+
},
209+
VolumeMounts: []corev1.VolumeMount{
210+
testVolumeMount("test-secret", "test-path"),
211+
testVolumeMount("test-configmap", "test-path"),
212+
testVolumeMount("test-pvc", "test-path"),
213+
},
214+
EnvFromSource: []corev1.EnvFromSource{
215+
testEnvFromSource("test-envfrom"),
216+
},
217+
},
218+
errRegexp: `auto-mounted volumes from \(secret 'test-secret', configmap 'test-configmap', pvc 'test-pvc'\) have the same mount path`,
219+
},
220+
}
221+
for _, tt := range tests {
222+
t.Run(tt.name, func(t *testing.T) {
223+
actualResources, err := mergeProjectedVolumes(tt.inputResources)
224+
if tt.errRegexp != "" {
225+
if !assert.Error(t, err) {
226+
return
227+
}
228+
assert.Regexp(t, tt.errRegexp, err.Error(), "Error message should match")
229+
} else {
230+
if !assert.NoError(t, err, "Should not return error") {
231+
return
232+
}
233+
assert.True(t, cmp.Equal(tt.expectedResources.Volumes, actualResources.Volumes, resourcesDiffOpts), cmp.Diff(tt.expectedResources.Volumes, actualResources.Volumes, resourcesDiffOpts))
234+
assert.True(t, cmp.Equal(tt.expectedResources.VolumeMounts, actualResources.VolumeMounts, resourcesDiffOpts), cmp.Diff(tt.expectedResources.VolumeMounts, actualResources.VolumeMounts, resourcesDiffOpts))
235+
assert.True(t, cmp.Equal(tt.expectedResources.EnvFromSource, actualResources.EnvFromSource, resourcesDiffOpts), cmp.Diff(tt.expectedResources.EnvFromSource, actualResources.EnvFromSource, resourcesDiffOpts))
236+
}
237+
})
238+
}
239+
}
240+
241+
func testSecretVolume(name string) corev1.Volume {
242+
return corev1.Volume{
243+
Name: name,
244+
VolumeSource: corev1.VolumeSource{
245+
Secret: &corev1.SecretVolumeSource{
246+
SecretName: name,
247+
},
248+
},
249+
}
250+
}
251+
252+
func testConfigmapVolume(name string) corev1.Volume {
253+
return corev1.Volume{
254+
Name: name,
255+
VolumeSource: corev1.VolumeSource{
256+
ConfigMap: &corev1.ConfigMapVolumeSource{
257+
LocalObjectReference: corev1.LocalObjectReference{
258+
Name: name,
259+
},
260+
},
261+
},
262+
}
263+
}
264+
265+
func testPVCVolume(name string) corev1.Volume {
266+
return corev1.Volume{
267+
Name: name,
268+
VolumeSource: corev1.VolumeSource{
269+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
270+
ClaimName: name,
271+
},
272+
},
273+
}
274+
}
275+
276+
func testVolumeMount(name, mountPath string) corev1.VolumeMount {
277+
return corev1.VolumeMount{
278+
Name: name,
279+
ReadOnly: true,
280+
MountPath: mountPath,
281+
}
282+
}
283+
284+
func testSubpathVolumeMount(name, mountPath, subpath string) corev1.VolumeMount {
285+
return corev1.VolumeMount{
286+
Name: name,
287+
ReadOnly: true,
288+
MountPath: path.Join(mountPath, subpath),
289+
SubPath: subpath,
290+
}
291+
}
292+
293+
func testEnvFromSource(name string) corev1.EnvFromSource {
294+
return corev1.EnvFromSource{
295+
ConfigMapRef: &corev1.ConfigMapEnvSource{
296+
LocalObjectReference: corev1.LocalObjectReference{
297+
Name: name,
298+
},
299+
},
300+
}
301+
}
302+
303+
func testProjectedVolume(name string, secretNames, configmapNames []string) corev1.Volume {
304+
vol := corev1.Volume{
305+
Name: name,
306+
VolumeSource: corev1.VolumeSource{
307+
Projected: &corev1.ProjectedVolumeSource{
308+
DefaultMode: pointer.Int32(0640),
309+
},
310+
},
311+
}
312+
for _, secretName := range secretNames {
313+
vol.Projected.Sources = append(vol.Projected.Sources, corev1.VolumeProjection{
314+
Secret: &corev1.SecretProjection{
315+
LocalObjectReference: corev1.LocalObjectReference{
316+
Name: secretName,
317+
},
318+
},
319+
})
320+
}
321+
for _, configmapName := range configmapNames {
322+
vol.Projected.Sources = append(vol.Projected.Sources, corev1.VolumeProjection{
323+
ConfigMap: &corev1.ConfigMapProjection{
324+
LocalObjectReference: corev1.LocalObjectReference{
325+
Name: configmapName,
326+
},
327+
},
328+
})
329+
}
330+
331+
return vol
332+
}

0 commit comments

Comments
 (0)