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  }