Skip to content

Commit e1178c4

Browse files
committed
optimize checks for overlapping in projected
1 parent 1853742 commit e1178c4

File tree

2 files changed

+418
-445
lines changed

2 files changed

+418
-445
lines changed

pkg/api/pod/warnings.go

+81-52
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"fmt"
2222
"os"
23-
"sort"
2423
"strings"
2524

2625
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -380,119 +379,149 @@ func warningsForWeightedPodAffinityTerms(terms []api.WeightedPodAffinityTerm, fi
380379
func warningsForOverlappingVirtualPaths(volumes []api.Volume) []string {
381380
var warnings []string
382381

382+
mkWarn := func(volName, volDesc, body string) string {
383+
return fmt.Sprintf("volume %q (%s): overlapping paths: %s", volName, volDesc, body)
384+
}
385+
383386
for _, v := range volumes {
384387
if v.ConfigMap != nil && v.ConfigMap.Items != nil {
385-
warnings = append(warnings, checkVolumeMappingForOverlap(extractPaths(v.ConfigMap.Items), fmt.Sprintf("volume %q (ConfigMap %q): overlapping paths", v.Name, v.ConfigMap.Name))...)
388+
overlaps := checkVolumeMappingForOverlap(extractPaths(v.ConfigMap.Items, ""))
389+
for _, ol := range overlaps {
390+
warnings = append(warnings, mkWarn(v.Name, fmt.Sprintf("ConfigMap %q", v.ConfigMap.Name), ol))
391+
}
386392
}
387393

388394
if v.Secret != nil && v.Secret.Items != nil {
389-
warnings = append(warnings, checkVolumeMappingForOverlap(extractPaths(v.Secret.Items), fmt.Sprintf("volume %q (Secret %q): overlapping paths", v.Name, v.Secret.SecretName))...)
395+
overlaps := checkVolumeMappingForOverlap(extractPaths(v.Secret.Items, ""))
396+
for _, ol := range overlaps {
397+
warnings = append(warnings, mkWarn(v.Name, fmt.Sprintf("Secret %q", v.Secret.SecretName), ol))
398+
}
390399
}
391400

392401
if v.DownwardAPI != nil && v.DownwardAPI.Items != nil {
393-
warnings = append(warnings, checkVolumeMappingForOverlap(extractPathsDownwardAPI(v.DownwardAPI.Items), fmt.Sprintf("volume %q (DownwardAPI): overlapping paths", v.Name))...)
402+
overlaps := checkVolumeMappingForOverlap(extractPathsDownwardAPI(v.DownwardAPI.Items, ""))
403+
for _, ol := range overlaps {
404+
warnings = append(warnings, mkWarn(v.Name, "DownwardAPI", ol))
405+
}
394406
}
395407

396408
if v.Projected != nil {
397-
var sourcePaths []string
398-
var errorMessage string
399-
allPaths := sets.New[string]()
409+
var sourcePaths []pathAndSource
410+
var allPaths []pathAndSource
400411

401412
for _, source := range v.Projected.Sources {
402413
if source == (api.VolumeProjection{}) {
403414
warnings = append(warnings, fmt.Sprintf("volume %q (Projected) has no sources provided", v.Name))
404415
continue
405416
}
417+
406418
switch {
407419
case source.ConfigMap != nil && source.ConfigMap.Items != nil:
408-
sourcePaths = extractPaths(source.ConfigMap.Items)
409-
errorMessage = fmt.Sprintf("volume %q (Projected ConfigMap %q): overlapping paths", v.Name, source.ConfigMap.Name)
420+
sourcePaths = extractPaths(source.ConfigMap.Items, fmt.Sprintf("ConfigMap %q", source.ConfigMap.Name))
410421
case source.Secret != nil && source.Secret.Items != nil:
411-
sourcePaths = extractPaths(source.Secret.Items)
412-
errorMessage = fmt.Sprintf("volume %q (Projected Secret %q): overlapping paths", v.Name, source.Secret.Name)
422+
sourcePaths = extractPaths(source.Secret.Items, fmt.Sprintf("Secret %q", source.Secret.Name))
413423
case source.DownwardAPI != nil && source.DownwardAPI.Items != nil:
414-
sourcePaths = extractPathsDownwardAPI(source.DownwardAPI.Items)
415-
errorMessage = fmt.Sprintf("volume %q (Projected DownwardAPI): overlapping paths", v.Name)
424+
sourcePaths = extractPathsDownwardAPI(source.DownwardAPI.Items, "DownwardAPI")
416425
case source.ServiceAccountToken != nil:
417-
sourcePaths = []string{source.ServiceAccountToken.Path}
418-
errorMessage = fmt.Sprintf("volume %q (Projected ServiceAccountToken): overlapping paths", v.Name)
426+
sourcePaths = []pathAndSource{{source.ServiceAccountToken.Path, "ServiceAccountToken"}}
419427
case source.ClusterTrustBundle != nil:
420-
sourcePaths = []string{source.ClusterTrustBundle.Path}
428+
name := ""
421429
if source.ClusterTrustBundle.Name != nil {
422-
errorMessage = fmt.Sprintf("volume %q (Projected ClusterTrustBundle %q): overlapping paths", v.Name, *source.ClusterTrustBundle.Name)
430+
name = *source.ClusterTrustBundle.Name
423431
} else {
424-
errorMessage = fmt.Sprintf("volume %q (Projected ClusterTrustBundle %q): overlapping paths", v.Name, *source.ClusterTrustBundle.SignerName)
432+
name = *source.ClusterTrustBundle.SignerName
425433
}
434+
sourcePaths = []pathAndSource{{source.ClusterTrustBundle.Path, fmt.Sprintf("ClusterTrustBundle %q", name)}}
426435
}
427436

428437
if len(sourcePaths) == 0 {
429438
continue
430439
}
431440

432-
warnings = append(warnings, checkVolumeMappingForOverlap(sourcePaths, errorMessage)...)
433-
434-
// remove duplicates path and sort the new array, so we can get predetermined result
435-
uniqueSourcePaths := sets.New[string](sourcePaths...).UnsortedList()
436-
orderedSourcePaths := append(uniqueSourcePaths, allPaths.UnsortedList()...)
437-
sort.Strings(orderedSourcePaths)
438-
warnings = append(warnings, checkVolumeMappingForOverlap(orderedSourcePaths, errorMessage)...)
439-
allPaths.Insert(uniqueSourcePaths...)
441+
for _, ps := range sourcePaths {
442+
ps.path = strings.TrimRight(ps.path, string(os.PathSeparator))
443+
if collisions := checkForOverlap(allPaths, ps); len(collisions) > 0 {
444+
for _, c := range collisions {
445+
warnings = append(warnings, mkWarn(v.Name, "Projected", fmt.Sprintf("%s with %s", ps.String(), c.String())))
446+
}
447+
}
448+
allPaths = append(allPaths, ps)
449+
}
440450
}
441451
}
442-
443452
}
444453
return warnings
445454
}
446455

447-
func extractPaths(mapping []api.KeyToPath) []string {
448-
result := make([]string, 0, len(mapping))
456+
// this lets us track a path and where it came from, for better errors
457+
type pathAndSource struct {
458+
path string
459+
source string
460+
}
461+
462+
func (ps pathAndSource) String() string {
463+
if ps.source != "" {
464+
return fmt.Sprintf("%q (%s)", ps.path, ps.source)
465+
}
466+
return fmt.Sprintf("%q", ps.path)
467+
}
468+
469+
func extractPaths(mapping []api.KeyToPath, source string) []pathAndSource {
470+
result := make([]pathAndSource, 0, len(mapping))
449471

450472
for _, v := range mapping {
451-
result = append(result, v.Path)
473+
result = append(result, pathAndSource{v.Path, source})
452474
}
453475
return result
454476
}
455477

456-
func extractPathsDownwardAPI(mapping []api.DownwardAPIVolumeFile) []string {
457-
result := make([]string, 0, len(mapping))
478+
func extractPathsDownwardAPI(mapping []api.DownwardAPIVolumeFile, source string) []pathAndSource {
479+
result := make([]pathAndSource, 0, len(mapping))
458480

459481
for _, v := range mapping {
460-
result = append(result, v.Path)
482+
result = append(result, pathAndSource{v.Path, source})
461483
}
462484
return result
463485
}
464486

465-
func checkVolumeMappingForOverlap(paths []string, warningMessage string) []string {
466-
var warnings []string
487+
func checkVolumeMappingForOverlap(paths []pathAndSource) []string {
467488
pathSeparator := string(os.PathSeparator)
468-
uniquePaths := sets.New[string]()
489+
var warnings []string
490+
var allPaths []pathAndSource
469491

470-
for _, path := range paths {
471-
normalizedPath := strings.TrimRight(path, pathSeparator)
472-
if collision := checkForOverlap(uniquePaths, normalizedPath); collision != "" {
473-
warnings = append(warnings, fmt.Sprintf("%s: %q with %q", warningMessage, normalizedPath, collision))
492+
for _, ps := range paths {
493+
ps.path = strings.TrimRight(ps.path, pathSeparator)
494+
if collisions := checkForOverlap(allPaths, ps); len(collisions) > 0 {
495+
for _, c := range collisions {
496+
warnings = append(warnings, fmt.Sprintf("%s with %s", ps.String(), c.String()))
497+
}
474498
}
475-
uniquePaths.Insert(normalizedPath)
499+
allPaths = append(allPaths, ps)
476500
}
477501

478502
return warnings
479503
}
480504

481-
func checkForOverlap(paths sets.Set[string], path string) string {
505+
func checkForOverlap(haystack []pathAndSource, needle pathAndSource) []pathAndSource {
482506
pathSeparator := string(os.PathSeparator)
483507

484-
for item := range paths {
508+
if needle.path == "" {
509+
return nil
510+
}
511+
512+
var result []pathAndSource
513+
for _, item := range haystack {
485514
switch {
486-
case item == "" || path == "":
487-
return ""
488-
case item == path:
489-
return item
490-
case strings.HasPrefix(item+pathSeparator, path):
491-
return item
492-
case strings.HasPrefix(path+pathSeparator, item):
493-
return item
515+
case item.path == "":
516+
continue
517+
case item == needle:
518+
result = append(result, item)
519+
case strings.HasPrefix(item.path+pathSeparator, needle.path+pathSeparator):
520+
result = append(result, item)
521+
case strings.HasPrefix(needle.path+pathSeparator, item.path+pathSeparator):
522+
result = append(result, item)
494523
}
495524
}
496525

497-
return ""
526+
return result
498527
}

0 commit comments

Comments
 (0)