Change:
<!-- Please only use this template for submitting enhancement requests --What would you like to be : Unless disabled, the tests will prepull all images when running. They do so using the . I think there's the opportunity to improve the runtime of the test suite by prepulling the images in parallel. Why is this needed: Its always nicer to have faster tests :) Specifically for users running locally, it's nice to have the image prepull occur as quick as possible. That being said, we should also be cognizant of any tradeoffs associated with prepulling the images in parallel. If the code is substantially more complex, we would need a large improvement in test speed to justify it. I'm hopeful this discussion could be had once we have a proof of concept diff (which I think should be fairly minimal effort to create).
/sig node
I think this could be a good first issue for someone looking to get involved with contributing to sig-node :) Please post if you plan on working on it, so that there aren't multiple people tackling it.
I'd be interested! I'm looking for a first issue and think this would be a good one
Wonderful! Want to reach out to me on Kubernetes slack? I'm on there too :)
:wave: hiya ! Are you still thinking you could be interested in working on this? Lmk if so, and we can chat about next steps? If not, perhaps I'll open it back up to a newcomer looking for a first commit?
I'm going to open this back up for someone looking for a first commit. Lmk if you're still interest in getting sig-node, and happy to find an issue for you :)
Opening this back up for folks :) I'm happy to mentor whoever picks this up if that'd be helpful :) Ping me on k8s-slack, where I'm also
Can you assign this to meI'd be interested!
/assign
Awesome - feel free to reach out on k8s-slack if you have questions, or if you already have a plan, feel free to tag me in the pull request. Thanks :)
/lifecycle active /priority important-longterm

Discussion:
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", <ins> "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", </ins> "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",

package e2enode import ( <ins> "context" </ins> "fmt" "os" "os/exec" "os/user" <ins> "sync" </ins> "time" "k8s.io/klog" <ins> utilerrors "k8s.io/apimachinery/pkg/util/errors" </ins> "k8s.io/apimachinery/pkg/util/sets" internalapi "k8s.io/cri-api/pkg/apis" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"

maxImagePullRetries = 5 // Sleep duration between image pull retry attempts. imagePullRetryDelay = time.Second <ins> // Number of parallel count to pull images. maxParallelImagePullCount = 5 </ins> ) // NodeImageWhiteList is a list of images used in node e2e test. These images will be prepulled

} images := framework.ImageWhiteList.List() klog.V(4).Infof("Pre-pulling images with %s %+v", puller.Name(), images) <del> for _, image := range images { var ( err    error output []byte ) for i := 0; i < maxImagePullRetries; i++ { if i > 0 { time.Sleep(imagePullRetryDelay) } if output, err = puller.Pull(image); err == nil { break </del> <ins> imageCh := make(chan int, len(images)) for i := range images { imageCh <- i } close(imageCh) pullErrs := make([]error, len(images)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() parallelImagePullCount := maxParallelImagePullCount if len(images) < parallelImagePullCount { parallelImagePullCount = len(images) } var wg sync.WaitGroup wg.Add(parallelImagePullCount) for i := 0; i < parallelImagePullCount; i++ { go func() { defer wg.Done() for i := range imageCh { var ( pullErr error output  []byte ) for retryCount := 0; retryCount < maxImagePullRetries; retryCount++ { select { case <-ctx.Done(): return default: } if retryCount > 0 { time.Sleep(imagePullRetryDelay) } if output, pullErr = puller.Pull(images[i]); pullErr == nil { break } klog.Warningf("Failed to pull %s as user %q, retrying in %s (%d of %d): %v", images[i], usr.Username, imagePullRetryDelay.String(), retryCount+1, maxImagePullRetries, pullErr) } if pullErr != nil { klog.Warningf("Could not pre-pull image %s %v output: %s", images[i], pullErr, output) pullErrs[i] = pullErr cancel() return } </ins> } <del> klog.Warningf("Failed to pull %s as user %q, retrying in %s (%d of %d): %v", image, usr.Username, imagePullRetryDelay.String(), i+1, maxImagePullRetries, err) } if err != nil { klog.Warningf("Could not pre-pull image %s %v output: %s", image, err, output) return err } </del> <ins> }() </ins> } <del> return nil </del> <ins> wg.Wait() return utilerrors.NewAggregate(pullErrs) </ins> } // getGPUDevicePluginImage returns the image of GPU device plugin.