-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Warn if user is not using a supported platform (#7381)
* Add prereqs package * Incorporate prereq check into the clients * gazelle * gazelle fix * linter * Add tests * minor change in test * finish up tests * gazelle * error during platform detection does not cause client to fail fast Co-authored-by: dv8silencer <15720668+dv8silencer@users.noreply.github.com>
- Loading branch information
1 parent
437bab7
commit 7d9a706
Showing
9 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_test") | ||
load("@prysm//tools/go:def.bzl", "go_library") | ||
|
||
go_library( | ||
name = "go_default_library", | ||
srcs = ["prereq.go"], | ||
importpath = "github.com/prysmaticlabs/prysm/shared/prereq", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"@com_github_pkg_errors//:go_default_library", | ||
"@com_github_sirupsen_logrus//:go_default_library", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "go_default_test", | ||
srcs = ["prereq_test.go"], | ||
embed = [":go_default_library"], | ||
deps = [ | ||
"//shared/testutil/require:go_default_library", | ||
"@com_github_pkg_errors//:go_default_library", | ||
"@com_github_sirupsen_logrus//hooks/test:go_default_library", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package prereq | ||
|
||
import ( | ||
"context" | ||
"os/exec" | ||
"runtime" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type platform struct { | ||
os string | ||
arch string | ||
majorVersion int | ||
minorVersion int | ||
} | ||
|
||
var ( | ||
// execShellOutput has execShellOutputFunc as the default but can be changed for testing purposes. | ||
execShellOutput func(ctx context.Context, command string, args ...string) (string, error) = execShellOutputFunc | ||
runtimeOS = runtime.GOOS | ||
runtimeArch = runtime.GOARCH | ||
) | ||
|
||
// execShellOutputFunc passes a command and args to exec.CommandContext and returns the result as a string | ||
func execShellOutputFunc(ctx context.Context, command string, args ...string) (string, error) { | ||
result, err := exec.CommandContext(ctx, command, args...).Output() | ||
if err != nil { | ||
return "", errors.Wrap(err, "error in command execution") | ||
} | ||
return string(result), nil | ||
} | ||
|
||
func getSupportedPlatforms() []platform { | ||
return []platform{ | ||
{os: "linux", arch: "amd64"}, | ||
{os: "linux", arch: "arm64"}, | ||
{os: "darwin", arch: "amd64", majorVersion: 10, minorVersion: 14}, | ||
{os: "windows", arch: "amd64"}, | ||
} | ||
} | ||
|
||
// parseVersion takes a string and splits it using sep separator, and outputs a slice of integers | ||
// corresponding to version numbers. If it cannot find num level of versions, it returns an error | ||
func parseVersion(input string, num int, sep string) ([]int, error) { | ||
var version = make([]int, num) | ||
components := strings.Split(input, sep) | ||
for i, component := range components { | ||
components[i] = strings.TrimSpace(component) | ||
} | ||
if len(components) < num { | ||
return nil, errors.New("insufficient information about version") | ||
} | ||
for i := range version { | ||
var err error | ||
version[i], err = strconv.Atoi(components[i]) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "error during conversion") | ||
} | ||
} | ||
return version, nil | ||
} | ||
|
||
// meetsMinPlatformReqs returns true if the runtime matches any on the list of supported platforms | ||
func meetsMinPlatformReqs(ctx context.Context) (bool, error) { | ||
okPlatforms := getSupportedPlatforms() | ||
for _, platform := range okPlatforms { | ||
if runtimeOS == platform.os && runtimeArch == platform.arch { | ||
// If MacOS we make sure it meets the minimum version cutoff | ||
if runtimeOS == "darwin" { | ||
versionStr, err := execShellOutput(ctx, "uname", "-r") | ||
if err != nil { | ||
return false, errors.Wrap(err, "error obtaining MacOS version") | ||
} | ||
version, err := parseVersion(versionStr, 2, ".") | ||
if err != nil { | ||
return false, errors.Wrap(err, "error parsing version") | ||
} | ||
if version[0] != platform.majorVersion { | ||
return version[0] > platform.majorVersion, nil | ||
} | ||
if version[1] < platform.minorVersion { | ||
return false, nil | ||
} | ||
return true, nil | ||
} | ||
// Otherwise we have a match between runtime and our list of accepted platforms | ||
return true, nil | ||
} | ||
} | ||
return false, nil | ||
} | ||
|
||
// WarnIfNotSupported warns if the user's platform is not supported or if it fails to detect user's platform | ||
func WarnIfNotSupported(ctx context.Context) { | ||
supported, err := meetsMinPlatformReqs(ctx) | ||
if err != nil { | ||
log.Warnf("Failed to detect host platform: %v", err) | ||
return | ||
} | ||
if !supported { | ||
log.Warn("This platform is not supported. The following platforms are supported: Linux/AMD64," + | ||
" Linux/ARM64, Mac OS X/AMD64 (10.14+ only), and Windows/AMD64") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package prereq | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/prysmaticlabs/prysm/shared/testutil/require" | ||
logTest "github.com/sirupsen/logrus/hooks/test" | ||
) | ||
|
||
func TestMeetsMinPlatformReqs(t *testing.T) { | ||
// Linux | ||
runtimeOS = "linux" | ||
runtimeArch = "amd64" | ||
meetsReqs, err := meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, true, meetsReqs) | ||
require.NoError(t, err) | ||
runtimeArch = "arm64" | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, true, meetsReqs) | ||
require.NoError(t, err) | ||
// mips64 is not supported | ||
runtimeArch = "mips64" | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, false, meetsReqs) | ||
require.NoError(t, err) | ||
|
||
// Mac OS X | ||
// In this function we'll set the execShellOutput package variable to another function that will 'mock' the shell | ||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "", errors.New("error while running command") | ||
} | ||
runtimeOS = "darwin" | ||
runtimeArch = "amd64" | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, false, meetsReqs) | ||
require.ErrorContains(t, "error obtaining MacOS version", err) | ||
|
||
// Insufficient version | ||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "10.4", nil | ||
} | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, false, meetsReqs) | ||
require.NoError(t, err) | ||
|
||
// Just-sufficient older version | ||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "10.14", nil | ||
} | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, true, meetsReqs) | ||
require.NoError(t, err) | ||
|
||
// Sufficient newer version | ||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "10.15.7", nil | ||
} | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, true, meetsReqs) | ||
require.NoError(t, err) | ||
|
||
// Handling abnormal response | ||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "tiger.lion", nil | ||
} | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, false, meetsReqs) | ||
require.ErrorContains(t, "error parsing version", err) | ||
|
||
// Windows | ||
runtimeOS = "windows" | ||
runtimeArch = "amd64" | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, true, meetsReqs) | ||
require.NoError(t, err) | ||
runtimeArch = "arm64" | ||
meetsReqs, err = meetsMinPlatformReqs(context.Background()) | ||
require.Equal(t, false, meetsReqs) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestParseVersion(t *testing.T) { | ||
version, err := parseVersion("1.2.3", 3, ".") | ||
require.DeepEqual(t, version, []int{1, 2, 3}) | ||
require.NoError(t, err) | ||
|
||
version, err = parseVersion("6 .7 . 8 ", 3, ".") | ||
require.DeepEqual(t, version, []int{6, 7, 8}) | ||
require.NoError(t, err) | ||
|
||
version, err = parseVersion("10,3,5,6", 4, ",") | ||
require.DeepEqual(t, version, []int{10, 3, 5, 6}) | ||
require.NoError(t, err) | ||
|
||
version, err = parseVersion("4;6;8;10;11", 3, ";") | ||
require.DeepEqual(t, version, []int{4, 6, 8}) | ||
require.NoError(t, err) | ||
|
||
_, err = parseVersion("10.11", 3, ".") | ||
require.ErrorContains(t, "insufficient information about version", err) | ||
} | ||
|
||
func TestWarnIfNotSupported(t *testing.T) { | ||
runtimeOS = "linux" | ||
runtimeArch = "amd64" | ||
hook := logTest.NewGlobal() | ||
WarnIfNotSupported(context.Background()) | ||
require.LogsDoNotContain(t, hook, "Failed to detect host platform") | ||
require.LogsDoNotContain(t, hook, "platform is not supported") | ||
|
||
execShellOutput = func(ctx context.Context, command string, args ...string) (string, error) { | ||
return "tiger.lion", nil | ||
} | ||
runtimeOS = "darwin" | ||
runtimeArch = "amd64" | ||
hook = logTest.NewGlobal() | ||
WarnIfNotSupported(context.Background()) | ||
require.LogsContain(t, hook, "Failed to detect host platform") | ||
require.LogsContain(t, hook, "error parsing version") | ||
|
||
runtimeOS = "falseOs" | ||
runtimeArch = "falseArch" | ||
hook = logTest.NewGlobal() | ||
WarnIfNotSupported(context.Background()) | ||
require.LogsContain(t, hook, "platform is not supported") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters