Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding extension binary to local path #29557

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions pkg/test/extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ credentials to use are found in this code, and extensively documented in code
comments. The following environment variables are available to force certain
behaviors:

### Extension Binary

When developing locally, you may want to use a locally built extension
binary. You can override the binary from the registry by setting:

```
export EXTENSION_BINARY_OVERRIDE_HYPERKUBE="/home/sally/git/kubernetes/_output/bin/k8s-tests-ext"
```

This overrides all extension binaries registered for that image tag
(i.e. hyperkube). In the uncommon situation where an image tag is
providing multiple test binaries, you can more specifically override one
like this:

```
export EXTENSION_BINARY_OVERRIDE_HYPERKUBE_USR_BIN_K8S_TESTS_EXT_GZ="/home/sally/git/kubernetes/_output/bin/k8s-tests-ext"
```

### Caching

By default, binaries will be cached in `$XDG_CACHE_HOME/openshift-tests`
Expand Down
6 changes: 3 additions & 3 deletions pkg/test/extensions/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (b *TestBinary) Info(ctx context.Context) (*ExtensionInfo, error) {
command := exec.Command(b.binaryPath, "info")
infoJson, err := runWithTimeout(ctx, command, 10*time.Minute)
if err != nil {
return nil, fmt.Errorf("failed running '%s info': %w", b.binaryPath, err)
return nil, fmt.Errorf("failed running '%s info': %w\nOutput: %s", b.binaryPath, err, infoJson)
}
jsonBegins := bytes.IndexByte(infoJson, '{')
jsonEnds := bytes.LastIndexByte(infoJson, '}')
Expand Down Expand Up @@ -106,7 +106,7 @@ func (b *TestBinary) ListTests(ctx context.Context, envFlags EnvironmentFlags) (
command.Args = append(command.Args, envFlags.ArgStrings()...)
testList, err := runWithTimeout(ctx, command, 10*time.Minute)
if err != nil {
return nil, fmt.Errorf("failed running '%s list': %w", b.binaryPath, err)
return nil, fmt.Errorf("failed running '%s list': %w\nOutput: %s", b.binaryPath, err, testList)
}
buf := bytes.NewBuffer(testList)
for {
Expand Down Expand Up @@ -212,7 +212,7 @@ func (b *TestBinary) ListImages(ctx context.Context) (ImageSet, error) {
command := exec.Command(b.binaryPath, "images")
output, err := runWithTimeout(ctx, command, 10*time.Minute)
if err != nil {
return nil, fmt.Errorf("failed running '%s list': %w", b.binaryPath, err)
return nil, fmt.Errorf("failed running '%s list': %w\nOutput: %s", b.binaryPath, err, output)
}

var images []Image
Expand Down
35 changes: 34 additions & 1 deletion pkg/test/extensions/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package extensions

import (
"fmt"
"github.com/sirupsen/logrus"
"os"
"path"
"path/filepath"
Expand All @@ -11,6 +10,7 @@ import (

imagev1 "github.com/openshift/api/image/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/openshift/origin/test/extended/util"
)
Expand Down Expand Up @@ -98,6 +98,20 @@ func (provider *ExternalBinaryProvider) ExtractBinaryFromReleaseImage(tag, binar
return nil, fmt.Errorf("extraction path is not set, cleanup was already run")
}

// Allow overriding image path to an already existing local path, mostly useful
// for development.
if override := binaryPathOverride(tag, binary); override != "" {
logrus.WithFields(logrus.Fields{
"tag": tag,
"binary": binary,
"override": override,
}).Info("Found override for this extension")
return &TestBinary{
imageTag: tag,
binaryPath: override,
}, nil
}

// Resolve the image tag from the image stream.
image := ""
for _, t := range provider.imageStream.Spec.Tags {
Expand Down Expand Up @@ -187,3 +201,22 @@ func cleanOldCacheFiles(dir string) {
}
logrus.Infof("Cleaned up old cached data in %v", time.Since(start))
}

func binaryPathOverride(imageTag, binaryPath string) string {
safeEnvVar := strings.NewReplacer("/", "_", "-", "_", ".", "_")

// Check for a specific override for this binary path, less common but allows supporting
// images that have multiple test binaries.
// Example: EXTENSION_BINARY_OVERRIDE_HYPERKUBE_USR_BIN_K8S_TESTS_EXT_GZ
specificOverrideEnvVar := fmt.Sprintf("EXTENSION_BINARY_OVERRIDE_%s_%s",
strings.ToUpper(safeEnvVar.Replace(imageTag)),
strings.ToUpper(safeEnvVar.Replace(strings.TrimPrefix(binaryPath, "/"))),
)
if specificOverride := os.Getenv(specificOverrideEnvVar); specificOverride != "" {
return specificOverride
}

// Check for a global override for all binaries in this image
// Example: EXTENSION_BINARY_OVERRIDE_HYPERKUBE
return os.Getenv(fmt.Sprintf("EXTENSION_BINARY_OVERRIDE_%s", strings.ToUpper(safeEnvVar.Replace(imageTag))))
}
84 changes: 84 additions & 0 deletions pkg/test/extensions/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package extensions

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestBinaryPathOverride(t *testing.T) {
tests := []struct {
name string
imageTag string
binaryPath string
envVars map[string]string
expectedPath string
}{
{
name: "Global override for image",
imageTag: "hyperkube",
binaryPath: "/usr/bin/k8s-tests-ext",
envVars: map[string]string{
"EXTENSION_BINARY_OVERRIDE_HYPERKUBE": "/custom/global/path",
},
expectedPath: "/custom/global/path",
},
{
name: "Specific override for binary",
imageTag: "hyperkube",
binaryPath: "/usr/bin/k8s-tests-ext",
envVars: map[string]string{
"EXTENSION_BINARY_OVERRIDE_HYPERKUBE_USR_BIN_K8S_TESTS_EXT": "/custom/specific/path",
},
expectedPath: "/custom/specific/path",
},
{
name: "No overrides",
imageTag: "hyperkube",
binaryPath: "/usr/bin/k8s-tests-ext",
envVars: map[string]string{},
expectedPath: "",
},
{
name: "Specific override takes precedence over global",
imageTag: "hyperkube",
binaryPath: "/usr/bin/k8s-tests-ext",
envVars: map[string]string{
"EXTENSION_BINARY_OVERRIDE_HYPERKUBE": "/custom/global/path",
"EXTENSION_BINARY_OVERRIDE_HYPERKUBE_USR_BIN_K8S_TESTS_EXT": "/custom/specific/path",
},
expectedPath: "/custom/specific/path",
},
{
name: "Special characters in image and binary path",
imageTag: "special-image",
binaryPath: "/usr/local/bin/special-tests-1.2.3",
envVars: map[string]string{
"EXTENSION_BINARY_OVERRIDE_SPECIAL_IMAGE_USR_LOCAL_BIN_SPECIAL_TESTS_1_2_3": "/custom/path/for-special",
},
expectedPath: "/custom/path/for-special",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k, v := range tt.envVars {
err := os.Setenv(k, v)
assert.NoError(t, err)
}

result := binaryPathOverride(tt.imageTag, tt.binaryPath)

if result != tt.expectedPath {
t.Errorf("binaryPathOverride(%q, %q) = %q; want %q",
tt.imageTag, tt.binaryPath, result, tt.expectedPath)
}

for k := range tt.envVars {
err := os.Unsetenv(k)
assert.NoError(t, err)
}
})
}
}