1
//go:build linux && !remote
11
"github.com/containers/common/pkg/secrets"
12
"github.com/containers/podman/v5/libpod/define"
13
v1 "github.com/containers/podman/v5/pkg/k8s.io/api/core/v1"
14
"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/api/resource"
15
v12 "github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/apis/meta/v1"
16
"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/util/intstr"
17
"github.com/containers/podman/v5/pkg/specgen"
18
"github.com/docker/docker/pkg/meminfo"
19
"github.com/stretchr/testify/assert"
23
func createSecrets(t *testing.T, d string) *secrets.SecretsManager {
24
secretsManager, err := secrets.NewManager(d)
25
assert.NoError(t, err)
28
driverOpts := map[string]string{
32
storeOpts := secrets.StoreOptions{
33
DriverOpts: driverOpts,
36
for _, s := range k8sSecrets {
37
data, err := yaml.Marshal(s)
38
assert.NoError(t, err)
40
_, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, storeOpts)
41
assert.NoError(t, err)
47
func TestConfigMapVolumes(t *testing.T) {
52
configmaps []v1.ConfigMap
54
expectedItems map[string][]byte
57
"VolumeFromConfigmap",
60
VolumeSource: v1.VolumeSource{
61
ConfigMap: &v1.ConfigMapVolumeSource{
62
LocalObjectReference: v1.LocalObjectReference{
70
map[string][]byte{"myvar": []byte("bar")},
73
"VolumeFromBinaryConfigmap",
76
VolumeSource: v1.VolumeSource{
77
ConfigMap: &v1.ConfigMapVolumeSource{
78
LocalObjectReference: v1.LocalObjectReference{
86
map[string][]byte{"myvar": []byte("bin-bar")},
92
VolumeSource: v1.VolumeSource{
93
ConfigMap: &v1.ConfigMapVolumeSource{
94
LocalObjectReference: v1.LocalObjectReference{
101
`no such ConfigMap "fizz"`,
105
"ConfigmapMissingOptional",
108
VolumeSource: v1.VolumeSource{
109
ConfigMap: &v1.ConfigMapVolumeSource{
110
LocalObjectReference: v1.LocalObjectReference{
125
VolumeSource: v1.VolumeSource{
126
ConfigMap: &v1.ConfigMapVolumeSource{
127
LocalObjectReference: v1.LocalObjectReference{
136
map[string][]byte{"foo": []byte("bar"), "fizz": []byte("buzz")},
142
VolumeSource: v1.VolumeSource{
143
ConfigMap: &v1.ConfigMapVolumeSource{
144
LocalObjectReference: v1.LocalObjectReference{
148
Items: []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
154
map[string][]byte{"/custom/path": []byte("buzz")},
160
VolumeSource: v1.VolumeSource{
161
ConfigMap: &v1.ConfigMapVolumeSource{
162
LocalObjectReference: v1.LocalObjectReference{
163
Name: "multi-binary-item",
171
map[string][]byte{"foo": []byte("bin-bar"), "fizz": []byte("bin-buzz")},
174
"SpecificValueBinary",
177
VolumeSource: v1.VolumeSource{
178
ConfigMap: &v1.ConfigMapVolumeSource{
179
LocalObjectReference: v1.LocalObjectReference{
180
Name: "multi-binary-item",
183
Items: []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
189
map[string][]byte{"/custom/path": []byte("bin-buzz")},
195
VolumeSource: v1.VolumeSource{
196
ConfigMap: &v1.ConfigMapVolumeSource{
197
LocalObjectReference: v1.LocalObjectReference{
204
`the ConfigMap "dupe" is invalid: duplicate key "foo" present in data and binaryData`,
208
"DuplicateValuesSpecific",
211
VolumeSource: v1.VolumeSource{
212
ConfigMap: &v1.ConfigMapVolumeSource{
213
LocalObjectReference: v1.LocalObjectReference{
216
Items: []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
221
`the ConfigMap "dupe" is invalid: duplicate key "foo" present in data and binaryData`,
226
for _, test := range tests {
228
t.Run(test.name, func(t *testing.T) {
229
result, err := VolumeFromConfigMap(test.volume.ConfigMap, test.configmaps)
230
if test.errorMessage == "" {
231
assert.NoError(t, err)
232
assert.Equal(t, test.expectedItems, result.Items)
235
assert.Equal(t, test.errorMessage, err.Error())
241
func TestEnvVarsFrom(t *testing.T) {
243
secretsManager := createSecrets(t, d)
247
envFrom v1.EnvFromSource
248
options CtrSpecGenOptions
250
expected map[string]string
255
ConfigMapRef: &v1.ConfigMapEnvSource{
256
LocalObjectReference: v1.LocalObjectReference{
262
ConfigMaps: configMapList,
270
"ConfigMapDoesNotExist",
272
ConfigMapRef: &v1.ConfigMapEnvSource{
273
LocalObjectReference: v1.LocalObjectReference{
274
Name: "doesnotexist",
279
ConfigMaps: configMapList,
285
"OptionalConfigMapDoesNotExist",
287
ConfigMapRef: &v1.ConfigMapEnvSource{
288
LocalObjectReference: v1.LocalObjectReference{
289
Name: "doesnotexist",
295
ConfigMaps: configMapList,
301
"EmptyConfigMapList",
303
ConfigMapRef: &v1.ConfigMapEnvSource{
304
LocalObjectReference: v1.LocalObjectReference{
310
ConfigMaps: []v1.ConfigMap{},
316
"OptionalEmptyConfigMapList",
318
ConfigMapRef: &v1.ConfigMapEnvSource{
319
LocalObjectReference: v1.LocalObjectReference{
326
ConfigMaps: []v1.ConfigMap{},
334
SecretRef: &v1.SecretEnvSource{
335
LocalObjectReference: v1.LocalObjectReference{
341
SecretsManager: secretsManager,
349
"SecretDoesNotExist",
351
SecretRef: &v1.SecretEnvSource{
352
LocalObjectReference: v1.LocalObjectReference{
353
Name: "doesnotexist",
358
SecretsManager: secretsManager,
364
"SecretExistsMultipleDataEntries",
366
SecretRef: &v1.SecretEnvSource{
367
LocalObjectReference: v1.LocalObjectReference{
373
SecretsManager: secretsManager,
382
"SecretExistsMultipleStringDataEntries",
384
SecretRef: &v1.SecretEnvSource{
385
LocalObjectReference: v1.LocalObjectReference{
386
Name: "multi-stringdata",
391
SecretsManager: secretsManager,
400
"SecretExistsMultipleDataStringDataEntries",
402
SecretRef: &v1.SecretEnvSource{
403
LocalObjectReference: v1.LocalObjectReference{
404
Name: "multi-data-stringdata",
409
SecretsManager: secretsManager,
413
"myvardata": "foodata",
414
"myvar1": "foo1string", // stringData overwrites data
415
"myvarstring": "foostring",
419
"OptionalSecretDoesNotExist",
421
SecretRef: &v1.SecretEnvSource{
422
LocalObjectReference: v1.LocalObjectReference{
423
Name: "doesnotexist",
429
SecretsManager: secretsManager,
436
for _, test := range tests {
438
t.Run(test.name, func(t *testing.T) {
439
result, err := envVarsFrom(test.envFrom, &test.options)
440
assert.Equal(t, err == nil, test.succeed)
441
assert.Equal(t, test.expected, result)
446
func TestEnvVarValue(t *testing.T) {
448
secretsManager := createSecrets(t, d)
449
stringNumCPUs := strconv.Itoa(runtime.NumCPU())
451
mi, err := meminfo.Read()
453
stringMemTotal := strconv.FormatInt(mi.MemTotal, 10)
458
options CtrSpecGenOptions
466
ValueFrom: &v1.EnvVarSource{
467
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
468
LocalObjectReference: v1.LocalObjectReference{
476
ConfigMaps: configMapList,
482
"ContainerKeyDoesNotExistInConfigMap",
485
ValueFrom: &v1.EnvVarSource{
486
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
487
LocalObjectReference: v1.LocalObjectReference{
495
ConfigMaps: configMapList,
501
"OptionalContainerKeyDoesNotExistInConfigMap",
504
ValueFrom: &v1.EnvVarSource{
505
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
506
LocalObjectReference: v1.LocalObjectReference{
515
ConfigMaps: configMapList,
521
"ConfigMapDoesNotExist",
524
ValueFrom: &v1.EnvVarSource{
525
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
526
LocalObjectReference: v1.LocalObjectReference{
527
Name: "doesnotexist",
534
ConfigMaps: configMapList,
540
"OptionalConfigMapDoesNotExist",
543
ValueFrom: &v1.EnvVarSource{
544
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
545
LocalObjectReference: v1.LocalObjectReference{
546
Name: "doesnotexist",
554
ConfigMaps: configMapList,
560
"EmptyConfigMapList",
563
ValueFrom: &v1.EnvVarSource{
564
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
565
LocalObjectReference: v1.LocalObjectReference{
573
ConfigMaps: []v1.ConfigMap{},
579
"OptionalEmptyConfigMapList",
582
ValueFrom: &v1.EnvVarSource{
583
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
584
LocalObjectReference: v1.LocalObjectReference{
593
ConfigMaps: []v1.ConfigMap{},
602
ValueFrom: &v1.EnvVarSource{
603
SecretKeyRef: &v1.SecretKeySelector{
604
LocalObjectReference: v1.LocalObjectReference{
612
SecretsManager: secretsManager,
618
"ContainerKeyDoesNotExistInSecret",
621
ValueFrom: &v1.EnvVarSource{
622
SecretKeyRef: &v1.SecretKeySelector{
623
LocalObjectReference: v1.LocalObjectReference{
631
SecretsManager: secretsManager,
637
"OptionalContainerKeyDoesNotExistInSecret",
640
ValueFrom: &v1.EnvVarSource{
641
SecretKeyRef: &v1.SecretKeySelector{
642
LocalObjectReference: v1.LocalObjectReference{
651
SecretsManager: secretsManager,
657
"SecretDoesNotExist",
660
ValueFrom: &v1.EnvVarSource{
661
SecretKeyRef: &v1.SecretKeySelector{
662
LocalObjectReference: v1.LocalObjectReference{
663
Name: "doesnotexist",
670
SecretsManager: secretsManager,
676
"OptionalSecretDoesNotExist",
679
ValueFrom: &v1.EnvVarSource{
680
SecretKeyRef: &v1.SecretKeySelector{
681
LocalObjectReference: v1.LocalObjectReference{
682
Name: "doesnotexist",
690
SecretsManager: secretsManager,
696
"FieldRefMetadataName",
699
ValueFrom: &v1.EnvVarSource{
700
FieldRef: &v1.ObjectFieldSelector{
701
FieldPath: "metadata.name",
712
"FieldRefMetadataUID",
715
ValueFrom: &v1.EnvVarSource{
716
FieldRef: &v1.ObjectFieldSelector{
717
FieldPath: "metadata.uid",
722
PodID: "ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977",
725
"ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977",
728
"FieldRefMetadataLabelsExist",
731
ValueFrom: &v1.EnvVarSource{
732
FieldRef: &v1.ObjectFieldSelector{
733
FieldPath: "metadata.labels['label']",
738
Labels: map[string]string{"label": "label"},
744
"FieldRefMetadataLabelsEmpty",
747
ValueFrom: &v1.EnvVarSource{
748
FieldRef: &v1.ObjectFieldSelector{
749
FieldPath: "metadata.labels['label']",
754
Labels: map[string]string{"label": ""},
760
"FieldRefMetadataLabelsNotExist",
763
ValueFrom: &v1.EnvVarSource{
764
FieldRef: &v1.ObjectFieldSelector{
765
FieldPath: "metadata.labels['label']",
774
"FieldRefMetadataAnnotationsExist",
777
ValueFrom: &v1.EnvVarSource{
778
FieldRef: &v1.ObjectFieldSelector{
779
FieldPath: "metadata.annotations['annotation']",
784
Annotations: map[string]string{"annotation": "annotation"},
790
"FieldRefMetadataAnnotationsEmpty",
793
ValueFrom: &v1.EnvVarSource{
794
FieldRef: &v1.ObjectFieldSelector{
795
FieldPath: "metadata.annotations['annotation']",
800
Annotations: map[string]string{"annotation": ""},
806
"FieldRefMetadataAnnotationsNotExist",
809
ValueFrom: &v1.EnvVarSource{
810
FieldRef: &v1.ObjectFieldSelector{
811
FieldPath: "metadata.annotations['annotation']",
823
ValueFrom: &v1.EnvVarSource{
824
FieldRef: &v1.ObjectFieldSelector{
825
FieldPath: "metadata.annotations['annotation]",
837
ValueFrom: &v1.EnvVarSource{
838
FieldRef: &v1.ObjectFieldSelector{
839
FieldPath: "metadata.dummy['annotation']",
848
"FieldRefNotSupported",
851
ValueFrom: &v1.EnvVarSource{
852
FieldRef: &v1.ObjectFieldSelector{
853
FieldPath: "metadata.namespace",
862
"ResourceFieldRefNotSupported",
865
ValueFrom: &v1.EnvVarSource{
866
ResourceFieldRef: &v1.ResourceFieldSelector{
867
Resource: "limits.dummy",
876
"ResourceFieldRefMemoryDivisorNotValid",
879
ValueFrom: &v1.EnvVarSource{
880
ResourceFieldRef: &v1.ResourceFieldSelector{
881
Resource: "limits.memory",
882
Divisor: resource.MustParse("2M"),
891
"ResourceFieldRefCpuDivisorNotValid",
894
ValueFrom: &v1.EnvVarSource{
895
ResourceFieldRef: &v1.ResourceFieldSelector{
896
Resource: "limits.cpu",
897
Divisor: resource.MustParse("2m"),
906
"ResourceFieldRefNoDivisor",
909
ValueFrom: &v1.EnvVarSource{
910
ResourceFieldRef: &v1.ResourceFieldSelector{
911
Resource: "limits.memory",
916
Container: container,
922
"ResourceFieldRefMemoryDivisor",
925
ValueFrom: &v1.EnvVarSource{
926
ResourceFieldRef: &v1.ResourceFieldSelector{
927
Resource: "limits.memory",
928
Divisor: resource.MustParse("1Mi"),
933
Container: container,
936
strconv.Itoa(int(math.Ceil(float64(memoryInt) / 1024 / 1024))),
939
"ResourceFieldRefCpuDivisor",
942
ValueFrom: &v1.EnvVarSource{
943
ResourceFieldRef: &v1.ResourceFieldSelector{
944
Resource: "requests.cpu",
945
Divisor: resource.MustParse("1m"),
950
Container: container,
953
strconv.Itoa(int(float64(cpuInt) / 0.001)),
956
"ResourceFieldRefNoLimitMemory",
959
ValueFrom: &v1.EnvVarSource{
960
ResourceFieldRef: &v1.ResourceFieldSelector{
961
Resource: "limits.memory",
966
Container: v1.Container{
974
"ResourceFieldRefNoRequestMemory",
977
ValueFrom: &v1.EnvVarSource{
978
ResourceFieldRef: &v1.ResourceFieldSelector{
979
Resource: "requests.memory",
984
Container: v1.Container{
992
"ResourceFieldRefNoLimitCPU",
995
ValueFrom: &v1.EnvVarSource{
996
ResourceFieldRef: &v1.ResourceFieldSelector{
997
Resource: "limits.cpu",
1002
Container: v1.Container{
1010
"ResourceFieldRefNoRequestCPU",
1013
ValueFrom: &v1.EnvVarSource{
1014
ResourceFieldRef: &v1.ResourceFieldSelector{
1015
Resource: "requests.cpu",
1020
Container: v1.Container{
1029
for _, test := range tests {
1031
t.Run(test.name, func(t *testing.T) {
1032
result, err := envVarValue(test.envVar, &test.options)
1033
assert.Equal(t, err == nil, test.succeed)
1034
if test.expected == nilString {
1035
assert.Nil(t, result)
1037
assert.Equal(t, test.expected, *result)
1045
configMapList = []v1.ConfigMap{
1047
TypeMeta: v12.TypeMeta{
1050
ObjectMeta: v12.ObjectMeta{
1053
Data: map[string]string{
1058
TypeMeta: v12.TypeMeta{
1061
ObjectMeta: v12.ObjectMeta{
1064
Data: map[string]string{
1069
TypeMeta: v12.TypeMeta{
1072
ObjectMeta: v12.ObjectMeta{
1075
BinaryData: map[string][]byte{
1076
"myvar": []byte("bin-bar"),
1080
TypeMeta: v12.TypeMeta{
1083
ObjectMeta: v12.ObjectMeta{
1086
Data: map[string]string{
1092
TypeMeta: v12.TypeMeta{
1095
ObjectMeta: v12.ObjectMeta{
1096
Name: "multi-binary-item",
1098
BinaryData: map[string][]byte{
1099
"foo": []byte("bin-bar"),
1100
"fizz": []byte("bin-buzz"),
1104
TypeMeta: v12.TypeMeta{
1107
ObjectMeta: v12.ObjectMeta{
1110
BinaryData: map[string][]byte{
1111
"fiz": []byte("bin-buzz"),
1112
"foo": []byte("bin-bar"),
1114
Data: map[string]string{
1122
k8sSecrets = []v1.Secret{
1124
TypeMeta: v12.TypeMeta{
1127
ObjectMeta: v12.ObjectMeta{
1130
Data: map[string][]byte{
1131
"myvar": []byte("bar"),
1135
TypeMeta: v12.TypeMeta{
1138
ObjectMeta: v12.ObjectMeta{
1141
Data: map[string][]byte{
1142
"myvar": []byte("foo"),
1146
TypeMeta: v12.TypeMeta{
1149
ObjectMeta: v12.ObjectMeta{
1152
Data: map[string][]byte{
1153
"myvar": []byte("foo"),
1154
"myvar1": []byte("foo1"),
1158
TypeMeta: v12.TypeMeta{
1161
ObjectMeta: v12.ObjectMeta{
1162
Name: "multi-stringdata",
1164
StringData: map[string]string{
1165
"myvar": string("foo"),
1166
"myvar1": string("foo1"),
1170
TypeMeta: v12.TypeMeta{
1173
ObjectMeta: v12.ObjectMeta{
1174
Name: "multi-data-stringdata",
1176
Data: map[string][]byte{
1177
"myvardata": []byte("foodata"),
1178
"myvar1": []byte("foo1data"),
1180
StringData: map[string]string{
1181
"myvarstring": string("foostring"),
1182
"myvar1": string("foo1string"),
1188
cpuString = strconv.Itoa(cpuInt)
1189
memoryInt = 30000000
1190
memoryString = strconv.Itoa(memoryInt)
1191
container = v1.Container{
1193
Resources: v1.ResourceRequirements{
1194
Limits: v1.ResourceList{
1195
v1.ResourceCPU: resource.MustParse(cpuString),
1196
v1.ResourceMemory: resource.MustParse(memoryString),
1198
Requests: v1.ResourceList{
1199
v1.ResourceCPU: resource.MustParse(cpuString),
1200
v1.ResourceMemory: resource.MustParse(memoryString),
1206
func TestHttpLivenessProbe(t *testing.T) {
1209
specGenerator specgen.SpecGenerator
1210
container v1.Container
1211
restartPolicy string
1216
"HttpLivenessProbeUrlSetCorrectly",
1217
specgen.SpecGenerator{},
1219
LivenessProbe: &v1.Probe{
1220
Handler: v1.Handler{
1221
HTTPGet: &v1.HTTPGetAction{
1224
Port: intstr.FromInt(8080),
1232
"http://127.0.0.1:8080/health",
1235
"HttpLivenessProbeUrlUsesDefaults",
1236
specgen.SpecGenerator{},
1238
LivenessProbe: &v1.Probe{
1239
Handler: v1.Handler{
1240
HTTPGet: &v1.HTTPGetAction{
1241
Port: intstr.FromInt(80),
1248
"http://localhost:80/",
1251
"HttpLivenessProbeNamedPort",
1252
specgen.SpecGenerator{},
1254
LivenessProbe: &v1.Probe{
1255
Handler: v1.Handler{
1256
HTTPGet: &v1.HTTPGetAction{
1257
Port: intstr.FromString("httpPort"),
1261
Ports: []v1.ContainerPort{
1262
{Name: "servicePort", ContainerPort: 7000},
1263
{Name: "httpPort", ContainerPort: 8000},
1268
"http://localhost:8000/",
1272
for _, test := range tests {
1274
t.Run(test.name, func(t *testing.T) {
1275
err := setupLivenessProbe(&test.specGenerator, test.container, test.restartPolicy)
1277
assert.Equal(t, err == nil, test.succeed)
1278
assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedURL)
1284
func TestTCPLivenessProbe(t *testing.T) {
1287
specGenerator specgen.SpecGenerator
1288
container v1.Container
1289
restartPolicy string
1295
"TCPLivenessProbeNormal",
1296
specgen.SpecGenerator{},
1298
LivenessProbe: &v1.Probe{
1299
Handler: v1.Handler{
1300
TCPSocket: &v1.TCPSocketAction{
1302
Port: intstr.FromInt(8080),
1313
"TCPLivenessProbeHostUsesDefault",
1314
specgen.SpecGenerator{},
1316
LivenessProbe: &v1.Probe{
1317
Handler: v1.Handler{
1318
TCPSocket: &v1.TCPSocketAction{
1319
Port: intstr.FromInt(200),
1330
"TCPLivenessProbeUseNamedPort",
1331
specgen.SpecGenerator{},
1333
LivenessProbe: &v1.Probe{
1334
Handler: v1.Handler{
1335
TCPSocket: &v1.TCPSocketAction{
1336
Port: intstr.FromString("servicePort"),
1337
Host: "myservice.domain.com",
1341
Ports: []v1.ContainerPort{
1342
{ContainerPort: 6000},
1343
{Name: "servicePort", ContainerPort: 4000},
1344
{Name: "2ndServicePort", ContainerPort: 3000},
1349
"myservice.domain.com",
1353
"TCPLivenessProbeInvalidPortName",
1354
specgen.SpecGenerator{},
1356
LivenessProbe: &v1.Probe{
1357
Handler: v1.Handler{
1358
TCPSocket: &v1.TCPSocketAction{
1359
Port: intstr.FromString("3rdservicePort"),
1360
Host: "myservice.domain.com",
1364
Ports: []v1.ContainerPort{
1365
{ContainerPort: 6000},
1366
{Name: "servicePort", ContainerPort: 4000},
1367
{Name: "2ndServicePort", ContainerPort: 3000},
1372
"myservice.domain.com",
1376
"TCPLivenessProbeNormalWithOnFailureRestartPolicy",
1377
specgen.SpecGenerator{},
1379
LivenessProbe: &v1.Probe{
1380
Handler: v1.Handler{
1381
TCPSocket: &v1.TCPSocketAction{
1383
Port: intstr.FromInt(8080),
1395
for _, test := range tests {
1397
t.Run(test.name, func(t *testing.T) {
1398
err := setupLivenessProbe(&test.specGenerator, test.container, test.restartPolicy)
1399
assert.Equal(t, err == nil, test.succeed)
1401
assert.Equal(t, int(test.specGenerator.ContainerHealthCheckConfig.HealthCheckOnFailureAction), define.HealthCheckOnFailureActionRestart)
1402
assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedHost)
1403
assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedPort)