From 86b12a6cc26dbc8f7e23706212dce7352828a68f Mon Sep 17 00:00:00 2001 From: Kui Wang Date: Mon, 9 Jun 2025 16:36:19 +0800 Subject: [PATCH] support component-extension for openshift-tests so that it can get and run extenal cases besides default extension --- .../openshift-tests/images/images_command.go | 14 +++--- pkg/test/extensions/binary.go | 21 ++++++++- pkg/test/extensions/provider.go | 10 ++++- pkg/test/extensions/util.go | 45 ++++++++++++++++++- pkg/test/ginkgo/cmd_runsuite.go | 5 ++- 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/openshift-tests/images/images_command.go b/pkg/cmd/openshift-tests/images/images_command.go index 9661d31d5017..c2b8512299c6 100644 --- a/pkg/cmd/openshift-tests/images/images_command.go +++ b/pkg/cmd/openshift-tests/images/images_command.go @@ -80,7 +80,7 @@ func NewImagesCommand() *cobra.Command { if err := imagesetup.VerifyImages(); err != nil { return err } - lines, err := createImageMirrorForInternalImages(prefix, ref, !o.Upstream) + lines, err := createImageMirrorForInternalImages(prefix, ref, !o.Upstream, o.ComponentExtensions) if err != nil { return err } @@ -95,13 +95,15 @@ func NewImagesCommand() *cobra.Command { // this is a private flag for debugging only cmd.Flags().BoolVar(&o.Verify, "verify", o.Verify, "Verify the contents of the image mappings") cmd.Flags().MarkHidden("verify") + cmd.Flags().Var(extensions.NewStringToMap(&o.ComponentExtensions), "component-extension", "Set extension which is used for the component as = pairs (e.g. --component-extension=tag1:id1,tag2=id2 or --component-extension=tag1:id1 --component-extension=tag2=id2)") return cmd } type imagesOptions struct { - Repository string - Upstream bool - Verify bool + Repository string + Upstream bool + Verify bool + ComponentExtensions map[string]string } // createImageMirrorForInternalImages returns a list of 'oc image mirror' mappings from source to @@ -110,7 +112,7 @@ type imagesOptions struct { // of the original internal name and the index of the image in the array. Otherwise the mappings will // be set to mirror the location as defined in the test code into our official mirror, where the target // TAG is the hash described above. -func createImageMirrorForInternalImages(prefix string, ref reference.DockerImageReference, mirrored bool) ([]string, error) { +func createImageMirrorForInternalImages(prefix string, ref reference.DockerImageReference, mirrored bool, compExts map[string]string) ([]string, error) { source := ref.Exact() initialImageSets := []extensions.ImageSet{ @@ -122,7 +124,7 @@ func createImageMirrorForInternalImages(prefix string, ref reference.DockerImage // Extract all test binaries extractionContext, extractionContextCancel := context.WithTimeout(context.Background(), 30*time.Minute) defer extractionContextCancel() - cleanUpFn, externalBinaries, err := extensions.ExtractAllTestBinaries(extractionContext, 10) + cleanUpFn, externalBinaries, err := extensions.ExtractAllTestBinaries(extractionContext, 10, compExts) if err != nil { return nil, err } diff --git a/pkg/test/extensions/binary.go b/pkg/test/extensions/binary.go index 812004efcfa9..57f0bf84ad8f 100644 --- a/pkg/test/extensions/binary.go +++ b/pkg/test/extensions/binary.go @@ -34,6 +34,11 @@ type TestBinary struct { // The binary path to extract from the image binaryPath string + // the extension from which the openshift-tests selects case of the external binary. + // if it is empty (do not set parameter "--component-extension"), the "default" extension is used. + // if you do not want "default" extension, need to set it with parmater "--component-extension" + extension string + // Cache the info after gathering it info *ExtensionInfo } @@ -72,6 +77,9 @@ func (b *TestBinary) Info(ctx context.Context) (*ExtensionInfo, error) { logrus.Infof("Fetching info for %s", binName) command := exec.Command(b.binaryPath, "info") + if len(b.extension) != 0 { + command.Args = append(command.Args, fmt.Sprintf("--component=%s", b.extension)) + } infoJson, err := runWithTimeout(ctx, command, 10*time.Minute) if err != nil { return nil, fmt.Errorf("failed running '%s info': %w\nOutput: %s", b.binaryPath, err, infoJson) @@ -108,6 +116,9 @@ func (b *TestBinary) ListTests(ctx context.Context, envFlags EnvironmentFlags) ( command := exec.Command(b.binaryPath, "list", "-o", "jsonl") binLogger.Infof("Adding the following applicable flags to the list command: %s", envFlags.String()) command.Args = append(command.Args, envFlags.ArgStrings()...) + if len(b.extension) != 0 { + command.Args = append(command.Args, fmt.Sprintf("--component=%s", b.extension)) + } testList, err := runWithTimeout(ctx, command, 10*time.Minute) if err != nil { return nil, fmt.Errorf("failed running '%s list': %w\nOutput: %s", b.binaryPath, err, testList) @@ -158,6 +169,9 @@ func (b *TestBinary) RunTests(ctx context.Context, timeout time.Duration, env [] } args = append(args, "-o", "jsonl") command := exec.Command(b.binaryPath, args...) + if len(b.extension) != 0 { + command.Args = append(command.Args, fmt.Sprintf("--component=%s", b.extension)) + } if len(env) == 0 { env = os.Environ() } @@ -218,6 +232,9 @@ func (b *TestBinary) ListImages(ctx context.Context) (ImageSet, error) { logrus.Infof("Listing images for %q", binName) command := exec.Command(b.binaryPath, "images") + if len(b.extension) != 0 { + command.Args = append(command.Args, fmt.Sprintf("--component=%s", b.extension)) + } output, err := runWithTimeout(ctx, command, 10*time.Minute) if err != nil { return nil, fmt.Errorf("failed running '%s list': %w\nOutput: %s", b.binaryPath, err, output) @@ -244,7 +261,7 @@ func (b *TestBinary) ListImages(ctx context.Context) (ImageSet, error) { // ExtractAllTestBinaries determines the optimal release payload to use, and extracts all the external // test binaries from it, and returns a slice of them. -func ExtractAllTestBinaries(ctx context.Context, parallelism int) (func(), TestBinaries, error) { +func ExtractAllTestBinaries(ctx context.Context, parallelism int, compExts map[string]string) (func(), TestBinaries, error) { if parallelism < 1 { return nil, nil, errors.New("parallelism must be greater than zero") } @@ -352,7 +369,7 @@ func ExtractAllTestBinaries(ctx context.Context, parallelism int) (func(), TestB if !ok { return // Channel is closed } - testBinary, err := externalBinaryProvider.ExtractBinaryFromReleaseImage(b.imageTag, b.binaryPath) + testBinary, err := externalBinaryProvider.ExtractBinaryFromReleaseImage(b.imageTag, b.binaryPath, compExts) if err != nil { errCh <- err continue diff --git a/pkg/test/extensions/provider.go b/pkg/test/extensions/provider.go index e55ad3ca5e5c..c1e0b8ba9424 100644 --- a/pkg/test/extensions/provider.go +++ b/pkg/test/extensions/provider.go @@ -93,11 +93,16 @@ func (provider *ExternalBinaryProvider) Cleanup() { // Note: When developing openshift-tests on a non-Linux non-AMD64 computer (i.e. on Apple Silicon), external // binaries won't work. You would need to run it in a Linux environment (VM or container), and even then // override the payload selection with an aarch64 payload unless x86 emulation is enabled. -func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binary string) (*TestBinary, error) { +func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binary string, compExts map[string]string) (*TestBinary, error) { if provider.binPath == "" { return nil, fmt.Errorf("extraction path is not set, cleanup was already run") } + var ext string + if val, ok := compExts[tag]; ok { + ext = val + } + // Allow overriding image path to an already existing local path, mostly useful // for development. if override := binaryPathOverride(tag, binary); override != "" { @@ -109,6 +114,7 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar return &TestBinary{ imageTag: tag, binaryPath: override, + extension: ext, }, nil } @@ -134,6 +140,7 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar return &TestBinary{ imageTag: tag, binaryPath: binPath, + extension: ext, }, nil } @@ -184,6 +191,7 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar return &TestBinary{ binaryPath: extractedBinary, + extension: ext, }, nil } diff --git a/pkg/test/extensions/util.go b/pkg/test/extensions/util.go index 7868a8ed70d6..8a50fac67c16 100644 --- a/pkg/test/extensions/util.go +++ b/pkg/test/extensions/util.go @@ -7,7 +7,6 @@ import ( "debug/elf" "encoding/json" "fmt" - "github.com/sirupsen/logrus" "io" "os" "os/exec" @@ -18,6 +17,8 @@ import ( "strings" "time" + "github.com/sirupsen/logrus" + imagev1 "github.com/openshift/api/image/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -260,3 +261,45 @@ func extractReleaseImageStream(extractPath, releaseImage string, return is, releaseImage, nil } + +type StringToMap struct { + e *map[string]string +} + +func NewStringToMap(p *map[string]string) *StringToMap { + if *p == nil { + *p = make(map[string]string) + } + return &StringToMap{e: p} +} + +func (s2m *StringToMap) String() string { + var sb strings.Builder + first := true + for k, v := range *s2m.e { + if !first { + sb.WriteString(",") + } + sb.WriteString(fmt.Sprintf("%s=%s", k, v)) + first = false + } + return sb.String() +} + +func (s2m *StringToMap) Set(s string) error { + items := strings.Split(s, ",") + for _, item := range items { + kv := strings.SplitN(item, "=", 2) + if len(kv) != 2 { + return fmt.Errorf("invalid component-extenstion format, expected key=value: %q", item) + } + k := strings.TrimSpace(kv[0]) + v := strings.TrimSpace(kv[1]) + (*s2m.e)[k] = v + } + return nil +} + +func (s *StringToMap) Type() string { + return "stringToMap" +} diff --git a/pkg/test/ginkgo/cmd_runsuite.go b/pkg/test/ginkgo/cmd_runsuite.go index 45d57a408362..a7e4dffbc40e 100644 --- a/pkg/test/ginkgo/cmd_runsuite.go +++ b/pkg/test/ginkgo/cmd_runsuite.go @@ -79,6 +79,8 @@ type GinkgoRunSuiteOptions struct { IncludeSuccessOutput bool + ComponentExtensions map[string]string + CommandEnv []string DryRun bool @@ -118,6 +120,7 @@ func (o *GinkgoRunSuiteOptions) BindFlags(flags *pflag.FlagSet) { flags.IntVar(&o.ShardID, "shard-id", o.ShardID, "When tests are sharded across instances, which instance we are") flags.IntVar(&o.ShardCount, "shard-count", o.ShardCount, "Number of shards used to run tests across multiple instances") flags.StringVar(&o.ShardStrategy, "shard-strategy", o.ShardStrategy, "Which strategy to use for sharding (hash)") + flags.Var(extensions.NewStringToMap(&o.ComponentExtensions), "component-extension", "Set extension which is used for the component as = pairs (e.g. --component-extension=tag1:id1,tag2=id2 or --component-extension=tag1:id1 --component-extension=tag2=id2)") } func (o *GinkgoRunSuiteOptions) Validate() error { @@ -170,7 +173,7 @@ func (o *GinkgoRunSuiteOptions) Run(suite *TestSuite, junitSuiteName string, mon // Extract all test binaries extractionContext, extractionContextCancel := context.WithTimeout(context.Background(), 30*time.Minute) defer extractionContextCancel() - cleanUpFn, externalBinaries, err := extensions.ExtractAllTestBinaries(extractionContext, 10) + cleanUpFn, externalBinaries, err := extensions.ExtractAllTestBinaries(extractionContext, 10, o.ComponentExtensions) if err != nil { return err }