diff --git a/pkg/test/extensions/provider.go b/pkg/test/extensions/provider.go index b22f29a67452..1e6abc894277 100644 --- a/pkg/test/extensions/provider.go +++ b/pkg/test/extensions/provider.go @@ -6,6 +6,7 @@ import ( "path" "path/filepath" "strings" + "syscall" "time" imagev1 "github.com/openshift/api/image/v1" @@ -128,7 +129,21 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar // Define the path for the binary binPath := filepath.Join(provider.binPath, strings.TrimSuffix(filepath.Base(binary), ".gz")) - // Check if the binary already exists in the path + // Acquire a file lock to prevent concurrent extraction of the same binary. + // This is necessary when multiple openshift-tests processes share the same cache directory. + lockPath := binPath + ".lock" + lockFile, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644) + if err != nil { + return nil, fmt.Errorf("failed to create lock file %q: %w", lockPath, err) + } + defer lockFile.Close() + + if err := syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX); err != nil { + return nil, fmt.Errorf("failed to acquire lock on %q: %w", lockPath, err) + } + defer syscall.Flock(int(lockFile.Fd()), syscall.LOCK_UN) + + // Check if the binary already exists if _, err := os.Stat(binPath); err == nil { logrus.Infof("Using existing binary %s for tag %s", binPath, tag) return &TestBinary{ @@ -148,7 +163,7 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar // Verify that the extracted binary exists; "oc extract image" doesn't error when the path doesn't exist, // so we check that the extraction was successful via its existence - _, err := os.Stat(extractedBinary) + _, err = os.Stat(extractedBinary) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("extracted binary at path %q does not exist. the src path %q doesn't exist in image %q. note the version of origin needs to match the version of the cluster under test", diff --git a/pkg/test/extensions/util.go b/pkg/test/extensions/util.go index bf32d41398b2..fb2f2e0f7808 100644 --- a/pkg/test/extensions/util.go +++ b/pkg/test/extensions/util.go @@ -15,6 +15,7 @@ import ( "regexp" "runtime" "strings" + "syscall" "time" kapierrs "k8s.io/apimachinery/pkg/api/errors" @@ -237,7 +238,23 @@ func createBinPath(path string) error { func ExtractReleaseImageStream(extractPath, releaseImage string, registryAuthFilePath string) (*imagev1.ImageStream, string, error) { - if _, err := os.Stat(path.Join(extractPath, "image-references")); err != nil { + imageReferencesPath := path.Join(extractPath, "image-references") + + // Acquire a file lock to prevent concurrent extraction of image-references. + // This is necessary when multiple openshift-tests processes share the same cache directory. + lockPath := imageReferencesPath + ".lock" + lockFile, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644) + if err != nil { + return nil, "", fmt.Errorf("failed to create lock file %q: %w", lockPath, err) + } + defer lockFile.Close() + + if err := syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX); err != nil { + return nil, "", fmt.Errorf("failed to acquire lock on %q: %w", lockPath, err) + } + defer syscall.Flock(int(lockFile.Fd()), syscall.LOCK_UN) + + if _, err := os.Stat(imageReferencesPath); err != nil { if err := runImageExtract(releaseImage, "/release-manifests/image-references", extractPath, registryAuthFilePath); err != nil { return nil, "", fmt.Errorf("failed extracting image-references from %q: %w", releaseImage, err) }