k8s.io/kubernetes@v1.29.3/test/utils/image/csi_manifest.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package image 18 19 import ( 20 "bytes" 21 "fmt" 22 "io/fs" 23 "regexp" 24 "strings" 25 26 "k8s.io/apimachinery/pkg/util/yaml" 27 e2etestingmanifests "k8s.io/kubernetes/test/e2e/testing-manifests" 28 ) 29 30 // All of the image tags are of the format registry.k8s.io/sig-storage/hostpathplugin:v1.7.3. 31 var imageRE = regexp.MustCompile(`^(.*)/([^/:]*):(.*)$`) 32 33 // appendCSIImageConfigs extracts image repo, name and version from 34 // the YAML files under test/e2e/testing-manifests/storage-csi and 35 // creates new config entries for them. 36 func appendCSIImageConfigs(configs map[ImageID]Config) { 37 embeddedFS := e2etestingmanifests.GetE2ETestingManifestsFS().EmbeddedFS 38 39 // We add our images with ImageID numbers that start after the highest existing number. 40 index := ImageID(0) 41 for i := range configs { 42 if i > index { 43 index = i 44 } 45 } 46 47 err := fs.WalkDir(embeddedFS, "storage-csi", func(path string, d fs.DirEntry, err error) error { 48 if err != nil { 49 return err 50 } 51 if d.IsDir() || !strings.HasSuffix(path, ".yaml") { 52 return nil 53 } 54 data, err := embeddedFS.ReadFile(path) 55 if err != nil { 56 return err 57 } 58 59 // Split at the "---" separator before working on 60 // individual item. Only works for .yaml. 61 // 62 // We need to split ourselves because we need access 63 // to each original chunk of data for 64 // runtime.DecodeInto. kubectl has its own 65 // infrastructure for this, but that is a lot of code 66 // with many dependencies. 67 items := bytes.Split(data, []byte("\n---")) 68 for i, item := range items { 69 // We don't care what the actual type is. We just 70 // unmarshal into generic maps and then look up images. 71 var object interface{} 72 if err := yaml.Unmarshal(item, &object); err != nil { 73 return fmt.Errorf("decode item #%d in %s: %v", 74 i, path, err) 75 } 76 77 // Will be called for all image strings. 78 visit := func(value string) { 79 parts := imageRE.FindStringSubmatch(value) 80 if parts == nil { 81 return 82 } 83 config := Config{parts[1], parts[2], parts[3]} 84 for _, otherConfig := range configs { 85 if otherConfig == config { 86 return 87 } 88 } 89 index++ 90 configs[index] = config 91 } 92 93 // These paths match plain Pods and more complex types 94 // like Deployments. 95 findStrings(object, visit, "spec", "containers", "image") 96 findStrings(object, visit, "spec", "template", "spec", "containers", "image") 97 98 } 99 return nil 100 101 }) 102 if err != nil { 103 panic(err) 104 } 105 } 106 107 // findStrings recursively decends into an object along a certain path. Path 108 // elements are the named fields. If a field references a list, each of the 109 // list elements will be followed. 110 // 111 // Conceptually this is similar to a JSON path. 112 func findStrings(object interface{}, visit func(value string), path ...string) { 113 if len(path) == 0 { 114 // Found it. May or may not be a string, though. 115 if object, ok := object.(string); ok { 116 visit(object) 117 } 118 return 119 } 120 121 switch object := object.(type) { 122 case []interface{}: 123 // If we are in a list, check each entry. 124 for _, child := range object { 125 findStrings(child, visit, path...) 126 } 127 case map[string]interface{}: 128 // Follow path if possible 129 if child, ok := object[path[0]]; ok { 130 findStrings(child, visit, path[1:]...) 131 } 132 } 133 }