mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-29 20:24:31 +00:00
runtime: make kata-check check for newer release
Update `kata-check` to see if there is a newer version available for download. Useful for users installing static packages (without a package manager). Fixes: #734. Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
parent
7e33e36f4a
commit
1a77f69e15
@ -43,7 +43,8 @@ include $(ARCH_FILE)
|
||||
PROJECT_TYPE = kata
|
||||
PROJECT_NAME = Kata Containers
|
||||
PROJECT_TAG = kata-containers
|
||||
PROJECT_URL = https://github.com/kata-containers
|
||||
PROJECT_ORG = $(PROJECT_TAG)
|
||||
PROJECT_URL = https://github.com/$(PROJECT_ORG)
|
||||
PROJECT_BUG_URL = $(PROJECT_URL)/kata-containers/issues/new
|
||||
|
||||
# list of scripts to install
|
||||
@ -628,6 +629,7 @@ $(GENERATED_FILES): %: %.in $(MAKEFILE_LIST) VERSION .git-commit
|
||||
-e "s|@PKGRUNDIR@|$(PKGRUNDIR)|g" \
|
||||
-e "s|@NETMONPATH@|$(NETMONPATH)|g" \
|
||||
-e "s|@PROJECT_BUG_URL@|$(PROJECT_BUG_URL)|g" \
|
||||
-e "s|@PROJECT_ORG@|$(PROJECT_ORG)|g" \
|
||||
-e "s|@PROJECT_URL@|$(PROJECT_URL)|g" \
|
||||
-e "s|@PROJECT_NAME@|$(PROJECT_NAME)|g" \
|
||||
-e "s|@PROJECT_TAG@|$(PROJECT_TAG)|g" \
|
||||
|
@ -25,6 +25,9 @@ const projectPrefix = "@PROJECT_TYPE@"
|
||||
// original URL for this project
|
||||
const projectURL = "@PROJECT_URL@"
|
||||
|
||||
// Project URL's organisation name
|
||||
const projectORG = "@PROJECT_ORG@"
|
||||
|
||||
const defaultRootDirectory = "@PKGRUNDIR@"
|
||||
|
||||
// commit is the git commit the runtime is compiled from.
|
||||
|
@ -71,6 +71,9 @@ const (
|
||||
genericCPUFlagsTag = "flags" // nolint: varcheck, unused, deadcode
|
||||
genericCPUVendorField = "vendor_id" // nolint: varcheck, unused, deadcode
|
||||
genericCPUModelField = "model name" // nolint: varcheck, unused, deadcode
|
||||
|
||||
// If set, do not perform any network checks
|
||||
noNetworkEnvVar = "KATA_CHECK_NO_NETWORK"
|
||||
)
|
||||
|
||||
// variables rather than consts to allow tests to modify them
|
||||
@ -307,14 +310,71 @@ var kataCheckCLICommand = cli.Command{
|
||||
Usage: "tests if system can run " + project,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "verbose, v",
|
||||
Usage: "display the list of checks performed",
|
||||
Name: "check-version-only",
|
||||
Usage: "Only compare the current and latest available versions (requires network, non-root only)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "include-all-releases",
|
||||
Usage: "Don't filter out pre-release release versions",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-network-checks, n",
|
||||
Usage: "Do not run any checks using the network",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "only-list-releases",
|
||||
Usage: "Only list newer available releases (non-root only)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "strict, s",
|
||||
Usage: "perform strict checking",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "verbose, v",
|
||||
Usage: "display the list of checks performed",
|
||||
},
|
||||
},
|
||||
Description: fmt.Sprintf(`tests if system can run %s and version is current.
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
|
||||
- %s: If set to any value, act as if "--no-network-checks" was specified.
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
- Perform basic checks:
|
||||
|
||||
$ %s %s
|
||||
|
||||
- Local basic checks only:
|
||||
|
||||
$ %s %s --no-network-checks
|
||||
|
||||
- Perform further checks:
|
||||
|
||||
$ sudo %s %s
|
||||
|
||||
- Just check if a newer version is available:
|
||||
|
||||
$ %s %s --check-version-only
|
||||
|
||||
- List available releases (shows output in format "version;release-date;url"):
|
||||
|
||||
$ %s %s --only-list-releases
|
||||
|
||||
- List all available releases (includes pre-release versions):
|
||||
|
||||
$ %s %s --only-list-releases --include-all-releases
|
||||
`,
|
||||
project,
|
||||
noNetworkEnvVar,
|
||||
name, checkCmd,
|
||||
name, checkCmd,
|
||||
name, checkCmd,
|
||||
name, checkCmd,
|
||||
name, checkCmd,
|
||||
name, checkCmd,
|
||||
),
|
||||
|
||||
Action: func(context *cli.Context) error {
|
||||
verbose := context.Bool("verbose")
|
||||
@ -329,6 +389,28 @@ var kataCheckCLICommand = cli.Command{
|
||||
span, _ := katautils.Trace(ctx, "kata-check")
|
||||
defer span.Finish()
|
||||
|
||||
if context.Bool("no-network-checks") == false && os.Getenv(noNetworkEnvVar) == "" {
|
||||
cmd := RelCmdCheck
|
||||
|
||||
if context.Bool("only-list-releases") {
|
||||
cmd = RelCmdList
|
||||
}
|
||||
|
||||
if os.Geteuid() == 0 {
|
||||
kataLog.Warn("Not running network checks as super user")
|
||||
} else {
|
||||
|
||||
err = HandleReleaseVersions(cmd, version, context.Bool("include-all-releases"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if context.Bool("check-version-only") || context.Bool("only-list-releases") {
|
||||
return nil
|
||||
}
|
||||
|
||||
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
|
||||
if !ok {
|
||||
return errors.New("kata-check: cannot determine runtime config")
|
||||
|
410
src/runtime/cli/release.go
Normal file
410
src/runtime/cli/release.go
Normal file
@ -0,0 +1,410 @@
|
||||
// Copyright (c) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
)
|
||||
|
||||
type ReleaseCmd int
|
||||
|
||||
type releaseDetails struct {
|
||||
version semver.Version
|
||||
date string
|
||||
url string
|
||||
filename string
|
||||
}
|
||||
|
||||
const (
|
||||
// A release URL is expected to be prefixed with this value
|
||||
projectAPIURL = "https://api.github.com/repos/" + projectORG
|
||||
|
||||
releasesSuffix = "/releases"
|
||||
downloadsSuffix = releasesSuffix + "/download"
|
||||
|
||||
// Kata 1.x
|
||||
kata1xRepo = "runtime"
|
||||
kataLegacyReleaseURL = projectAPIURL + "/" + kata1xRepo + releasesSuffix
|
||||
kataLegacyDownloadURL = projectURL + "/" + kata1xRepo + downloadsSuffix
|
||||
|
||||
// Kata 2.x or newer
|
||||
kata2xRepo = "kata-containers"
|
||||
kataReleaseURL = projectAPIURL + "/" + kata2xRepo + releasesSuffix
|
||||
kataDownloadURL = projectURL + "/" + kata2xRepo + downloadsSuffix
|
||||
|
||||
// Environment variable that can be used to override a release URL
|
||||
ReleaseURLEnvVar = "KATA_RELEASE_URL"
|
||||
|
||||
RelCmdList ReleaseCmd = iota
|
||||
RelCmdCheck ReleaseCmd = iota
|
||||
|
||||
msgNoReleases = "No releases available"
|
||||
msgNoNewerRelease = "No newer release available"
|
||||
errNoNetChecksAsRoot = "No network checks allowed running as super user"
|
||||
)
|
||||
|
||||
func (c ReleaseCmd) Valid() bool {
|
||||
switch c {
|
||||
case RelCmdCheck, RelCmdList:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func downloadURLIsValid(url string) error {
|
||||
if url == "" {
|
||||
return errors.New("URL cannot be blank")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(url, kataDownloadURL) ||
|
||||
strings.HasPrefix(url, kataLegacyDownloadURL) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Download URL %q is not valid", url)
|
||||
}
|
||||
|
||||
func releaseURLIsValid(url string) error {
|
||||
if url == "" {
|
||||
return errors.New("URL cannot be blank")
|
||||
}
|
||||
|
||||
if url == kataReleaseURL || url == kataLegacyReleaseURL {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Release URL %q is not valid", url)
|
||||
}
|
||||
|
||||
func getReleaseURL(currentVersion semver.Version) (url string, err error) {
|
||||
major := currentVersion.Major
|
||||
|
||||
if major == 0 {
|
||||
return "", fmt.Errorf("invalid current version: %v", currentVersion)
|
||||
} else if major == 1 {
|
||||
url = kataLegacyReleaseURL
|
||||
} else {
|
||||
url = kataReleaseURL
|
||||
}
|
||||
|
||||
if value := os.Getenv(ReleaseURLEnvVar); value != "" {
|
||||
url = value
|
||||
}
|
||||
|
||||
if err := releaseURLIsValid(url); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func ignoreRelease(release releaseDetails, includeAll bool) bool {
|
||||
if includeAll {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(release.version.Pre) > 0 {
|
||||
// Pre-releases are ignored by default
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns a release version and release object from the specified map.
|
||||
func makeRelease(release map[string]interface{}) (version string, details releaseDetails, err error) {
|
||||
key := "tag_name"
|
||||
|
||||
version, ok := release[key].(string)
|
||||
if ok != true {
|
||||
return "", details, fmt.Errorf("failed to find key %s in release data", key)
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
return "", details, fmt.Errorf("release version cannot be blank")
|
||||
}
|
||||
|
||||
releaseSemver, err := semver.Make(version)
|
||||
if err != nil {
|
||||
return "", details, fmt.Errorf("release %q has invalid semver version: %v", version, err)
|
||||
}
|
||||
|
||||
key = "assets"
|
||||
|
||||
assetsArray, ok := release[key].([]interface{})
|
||||
if ok != true {
|
||||
return "", details, fmt.Errorf("failed to find key %s in release version %q data", key, version)
|
||||
}
|
||||
|
||||
if len(assetsArray) == 0 {
|
||||
// GitHub auto-creates the source assets, but binaries have to
|
||||
// be built and uploaded for a release.
|
||||
return "", details, fmt.Errorf("no binary assets for release %q", version)
|
||||
}
|
||||
|
||||
var createDate string
|
||||
var filename string
|
||||
var downloadURL string
|
||||
|
||||
assets := assetsArray[0]
|
||||
|
||||
key = "browser_download_url"
|
||||
|
||||
downloadURL, ok = assets.(map[string]interface{})[key].(string)
|
||||
if ok != true {
|
||||
return "", details, fmt.Errorf("failed to find key %s in release version %q asset data", key, version)
|
||||
}
|
||||
|
||||
if err := downloadURLIsValid(downloadURL); err != nil {
|
||||
return "", details, err
|
||||
}
|
||||
|
||||
key = "name"
|
||||
|
||||
filename, ok = assets.(map[string]interface{})[key].(string)
|
||||
if ok != true {
|
||||
return "", details, fmt.Errorf("failed to find key %s in release version %q asset data", key, version)
|
||||
}
|
||||
|
||||
if filename == "" {
|
||||
return "", details, fmt.Errorf("Release %q asset missing filename", version)
|
||||
}
|
||||
|
||||
key = "created_at"
|
||||
|
||||
createDate, ok = assets.(map[string]interface{})[key].(string)
|
||||
if ok != true {
|
||||
return "", details, fmt.Errorf("failed to find key %s in release version %q asset data", key, version)
|
||||
}
|
||||
|
||||
if createDate == "" {
|
||||
return "", details, fmt.Errorf("Release %q asset missing creation date", version)
|
||||
}
|
||||
|
||||
details = releaseDetails{
|
||||
version: releaseSemver,
|
||||
date: createDate,
|
||||
url: downloadURL,
|
||||
filename: filename,
|
||||
}
|
||||
|
||||
return version, details, nil
|
||||
}
|
||||
|
||||
func readReleases(releasesArray []map[string]interface{}, includeAll bool) (versions []semver.Version,
|
||||
releases map[string]releaseDetails) {
|
||||
|
||||
releases = make(map[string]releaseDetails)
|
||||
|
||||
for _, release := range releasesArray {
|
||||
version, details, err := makeRelease(release)
|
||||
|
||||
// Don't error if makeRelease() fails to construct a release.
|
||||
// There are many reasons a release may not be considered
|
||||
// valid, so just ignore the invalid ones.
|
||||
if err != nil {
|
||||
kataLog.WithField("version", version).WithError(err).Debug("ignoring invalid release version")
|
||||
continue
|
||||
}
|
||||
|
||||
if ignoreRelease(details, includeAll) {
|
||||
continue
|
||||
}
|
||||
|
||||
versions = append(versions, details.version)
|
||||
releases[version] = details
|
||||
}
|
||||
|
||||
semver.Sort(versions)
|
||||
|
||||
return versions, releases
|
||||
}
|
||||
|
||||
// Note: Assumes versions is sorted in ascending order
|
||||
func findNewestRelease(currentVersion semver.Version, versions []semver.Version) (bool, semver.Version, error) {
|
||||
var candidates []semver.Version
|
||||
|
||||
if len(versions) == 0 {
|
||||
return false, semver.Version{}, errors.New("no versions available")
|
||||
}
|
||||
|
||||
for _, version := range versions {
|
||||
if currentVersion.GTE(version) {
|
||||
// Ignore older releases (and the current one!)
|
||||
continue
|
||||
}
|
||||
|
||||
candidates = append(candidates, version)
|
||||
}
|
||||
|
||||
count := len(candidates)
|
||||
|
||||
if count == 0 {
|
||||
return false, semver.Version{}, nil
|
||||
}
|
||||
|
||||
return true, candidates[count-1], nil
|
||||
}
|
||||
|
||||
func getReleases(releaseURL string, includeAll bool) ([]semver.Version, map[string]releaseDetails, error) {
|
||||
kataLog.WithField("url", releaseURL).Info("Looking for releases")
|
||||
|
||||
if os.Geteuid() == 0 {
|
||||
return nil, nil, errors.New(errNoNetChecksAsRoot)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Get(releaseURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
releasesArray := []map[string]interface{}{}
|
||||
|
||||
bytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read release details: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(bytes, &releasesArray); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unpack release details: %v", err)
|
||||
}
|
||||
|
||||
versions, releases := readReleases(releasesArray, includeAll)
|
||||
|
||||
return versions, releases, nil
|
||||
}
|
||||
|
||||
func getNewReleaseType(current semver.Version, latest semver.Version) (string, error) {
|
||||
if current.GT(latest) {
|
||||
return "", fmt.Errorf("current version %s newer than latest %s", current, latest)
|
||||
}
|
||||
|
||||
if current.EQ(latest) {
|
||||
return "", fmt.Errorf("current version %s and latest are same", current)
|
||||
}
|
||||
|
||||
var desc string
|
||||
|
||||
if latest.Major > current.Major {
|
||||
if len(latest.Pre) > 0 {
|
||||
desc = "major pre-release"
|
||||
} else {
|
||||
desc = "major"
|
||||
}
|
||||
} else if latest.Minor > current.Minor {
|
||||
if len(latest.Pre) > 0 {
|
||||
desc = "minor pre-release"
|
||||
} else {
|
||||
desc = "minor"
|
||||
}
|
||||
} else if latest.Patch > current.Patch {
|
||||
if len(latest.Pre) > 0 {
|
||||
desc = "patch pre-release"
|
||||
} else {
|
||||
desc = "patch"
|
||||
}
|
||||
} else if latest.Patch == current.Patch && len(latest.Pre) > 0 {
|
||||
desc = "pre-release"
|
||||
} else {
|
||||
return "", fmt.Errorf("BUG: unhandled scenario: current version: %s, latest version: %v", current, latest)
|
||||
}
|
||||
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func showLatestRelease(output *os.File, current semver.Version, details releaseDetails) error {
|
||||
latest := details.version
|
||||
|
||||
desc, err := getNewReleaseType(current, latest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(output, "Newer %s release available: %s (url: %v, date: %v)\n",
|
||||
desc,
|
||||
details.version, details.url, details.date)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listReleases(output *os.File, current semver.Version, versions []semver.Version, releases map[string]releaseDetails) error {
|
||||
for _, version := range versions {
|
||||
details, ok := releases[version.String()]
|
||||
if !ok {
|
||||
return fmt.Errorf("Release %v has no details", version)
|
||||
}
|
||||
|
||||
fmt.Fprintf(output, "%s;%s;%s\n", version, details.date, details.url)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleReleaseVersions(cmd ReleaseCmd, currentVersion string, includeAll bool) error {
|
||||
if !cmd.Valid() {
|
||||
return fmt.Errorf("invalid release command: %v", cmd)
|
||||
}
|
||||
|
||||
output := os.Stdout
|
||||
|
||||
currentSemver, err := semver.Make(currentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("BUG: Current version of %s (%s) has invalid SemVer version: %v", name, currentVersion, err)
|
||||
}
|
||||
|
||||
releaseURL, err := getReleaseURL(currentSemver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versions, releases, err := getReleases(releaseURL, includeAll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd == RelCmdList {
|
||||
return listReleases(output, currentSemver, versions, releases)
|
||||
}
|
||||
|
||||
if len(versions) == 0 {
|
||||
fmt.Fprintf(output, "%s\n", msgNoReleases)
|
||||
return nil
|
||||
}
|
||||
|
||||
available, newest, err := findNewestRelease(currentSemver, versions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !available {
|
||||
fmt.Fprintf(output, "%s\n", msgNoNewerRelease)
|
||||
return nil
|
||||
}
|
||||
|
||||
details, ok := releases[newest.String()]
|
||||
if !ok {
|
||||
return fmt.Errorf("Release %v has no details", newest)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return showLatestRelease(output, currentSemver, details)
|
||||
}
|
498
src/runtime/cli/release_test.go
Normal file
498
src/runtime/cli/release_test.go
Normal file
@ -0,0 +1,498 @@
|
||||
// Copyright (c) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var currentSemver semver.Version
|
||||
var expectedReleasesURL string
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
currentSemver, err = semver.Make(version)
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to create semver for testing: %v", err))
|
||||
}
|
||||
|
||||
if currentSemver.Major == 1 {
|
||||
expectedReleasesURL = kataLegacyReleaseURL
|
||||
} else {
|
||||
expectedReleasesURL = kataReleaseURL
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseCmd(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for i, value := range []ReleaseCmd{RelCmdCheck, RelCmdList} {
|
||||
assert.True(value.Valid(), "test[%d]: %+v", i, value)
|
||||
}
|
||||
|
||||
for i, value := range []int{-1, 2, 42, 255} {
|
||||
invalid := ReleaseCmd(i)
|
||||
|
||||
assert.False(invalid.Valid(), "test[%d]: %+v", i, value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReleaseURL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
const kata1xURL = "https://api.github.com/repos/kata-containers/runtime/releases"
|
||||
const kata2xURL = "https://api.github.com/repos/kata-containers/kata-containers/releases"
|
||||
|
||||
type testData struct {
|
||||
currentVersion string
|
||||
expectError bool
|
||||
expectedURL string
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{"0.0.0", true, ""},
|
||||
{"1.0.0", false, kata1xURL},
|
||||
{"1.9999.9999", false, kata1xURL},
|
||||
{"2.0.0-alpha3", false, kata2xURL},
|
||||
{"2.9999.9999", false, kata2xURL},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
ver, err := semver.Make(d.currentVersion)
|
||||
msg = fmt.Sprintf("%s, version: %v, error: %v", msg, ver, err)
|
||||
|
||||
assert.NoError(err, msg)
|
||||
|
||||
url, err := getReleaseURL(ver)
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
} else {
|
||||
assert.NoError(err, msg)
|
||||
assert.Equal(url, d.expectedURL, msg)
|
||||
assert.True(strings.HasPrefix(url, projectAPIURL), msg)
|
||||
}
|
||||
}
|
||||
|
||||
url, err := getReleaseURL(currentSemver)
|
||||
assert.NoError(err)
|
||||
|
||||
assert.Equal(url, expectedReleasesURL)
|
||||
|
||||
assert.True(strings.HasPrefix(url, projectAPIURL))
|
||||
|
||||
}
|
||||
|
||||
func TestGetReleaseURLEnvVar(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
envVarValue string
|
||||
expectError bool
|
||||
expectedURL string
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{"", false, expectedReleasesURL},
|
||||
{"http://google.com", true, ""},
|
||||
{"https://katacontainers.io", true, ""},
|
||||
{"https://github.com/kata-containers/runtime/releases/latest", true, ""},
|
||||
{"https://github.com/kata-containers/kata-containers/releases/latest", true, ""},
|
||||
{expectedReleasesURL, false, expectedReleasesURL},
|
||||
}
|
||||
|
||||
assert.Equal(os.Getenv("KATA_RELEASE_URL"), "")
|
||||
defer os.Setenv("KATA_RELEASE_URL", "")
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
err := os.Setenv("KATA_RELEASE_URL", d.envVarValue)
|
||||
msg = fmt.Sprintf("%s, error: %v", msg, err)
|
||||
|
||||
assert.NoError(err, msg)
|
||||
|
||||
url, err := getReleaseURL(currentSemver)
|
||||
if d.expectError {
|
||||
assert.Errorf(err, msg)
|
||||
} else {
|
||||
assert.NoErrorf(err, msg)
|
||||
assert.Equal(d.expectedURL, url, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeRelease(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
release map[string]interface{}
|
||||
expectError bool
|
||||
expectedVersion string
|
||||
expectedDetails releaseDetails
|
||||
}
|
||||
|
||||
invalidRel1 := map[string]interface{}{"foo": 1}
|
||||
invalidRel2 := map[string]interface{}{"foo": "bar"}
|
||||
invalidRel3 := map[string]interface{}{"foo": true}
|
||||
|
||||
testDate := "2020-09-01T22:10:44Z"
|
||||
testRelVersion := "1.2.3"
|
||||
testFilename := "kata-static-1.12.0-alpha1-x86_64.tar.xz"
|
||||
testURL := fmt.Sprintf("https://github.com/kata-containers/runtime/releases/download/%s/%s", testRelVersion, testFilename)
|
||||
|
||||
testSemver, err := semver.Make(testRelVersion)
|
||||
assert.NoError(err)
|
||||
|
||||
invalidRelMissingVersion := map[string]interface{}{}
|
||||
|
||||
invalidRelInvalidVersion := map[string]interface{}{
|
||||
"tag_name": "not.valid.semver",
|
||||
}
|
||||
|
||||
invalidRelMissingAssets := map[string]interface{}{
|
||||
"tag_name": testRelVersion,
|
||||
"name": testFilename,
|
||||
"assets": []interface{}{},
|
||||
}
|
||||
|
||||
invalidAssetsMissingURL := []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": testFilename,
|
||||
"created_at": testDate,
|
||||
},
|
||||
}
|
||||
|
||||
invalidAssetsMissingFile := []interface{}{
|
||||
map[string]interface{}{
|
||||
"browser_download_url": testURL,
|
||||
"created_at": testDate,
|
||||
},
|
||||
}
|
||||
|
||||
invalidAssetsMissingDate := []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": testFilename,
|
||||
"browser_download_url": testURL,
|
||||
},
|
||||
}
|
||||
|
||||
validAssets := []interface{}{
|
||||
map[string]interface{}{
|
||||
"browser_download_url": testURL,
|
||||
"name": testFilename,
|
||||
"created_at": testDate,
|
||||
},
|
||||
}
|
||||
|
||||
invalidRelAssetsMissingURL := map[string]interface{}{
|
||||
"tag_name": testRelVersion,
|
||||
"name": testFilename,
|
||||
"assets": invalidAssetsMissingURL,
|
||||
}
|
||||
|
||||
invalidRelAssetsMissingFile := map[string]interface{}{
|
||||
"tag_name": testRelVersion,
|
||||
"name": testFilename,
|
||||
"assets": invalidAssetsMissingFile,
|
||||
}
|
||||
|
||||
invalidRelAssetsMissingDate := map[string]interface{}{
|
||||
"tag_name": testRelVersion,
|
||||
"name": testFilename,
|
||||
"assets": invalidAssetsMissingDate,
|
||||
}
|
||||
|
||||
validRel := map[string]interface{}{
|
||||
"tag_name": testRelVersion,
|
||||
"name": testFilename,
|
||||
"assets": validAssets,
|
||||
}
|
||||
|
||||
validReleaseDetails := releaseDetails{
|
||||
version: testSemver,
|
||||
date: testDate,
|
||||
url: testURL,
|
||||
filename: testFilename,
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{invalidRel1, true, "", releaseDetails{}},
|
||||
{invalidRel2, true, "", releaseDetails{}},
|
||||
{invalidRel3, true, "", releaseDetails{}},
|
||||
{invalidRelMissingVersion, true, "", releaseDetails{}},
|
||||
{invalidRelInvalidVersion, true, "", releaseDetails{}},
|
||||
{invalidRelMissingAssets, true, "", releaseDetails{}},
|
||||
{invalidRelAssetsMissingURL, true, "", releaseDetails{}},
|
||||
{invalidRelAssetsMissingFile, true, "", releaseDetails{}},
|
||||
{invalidRelAssetsMissingDate, true, "", releaseDetails{}},
|
||||
|
||||
{validRel, false, testRelVersion, validReleaseDetails},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
version, details, err := makeRelease(d.release)
|
||||
msg = fmt.Sprintf("%s, version: %v, details: %+v, error: %v", msg, version, details, err)
|
||||
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(err, msg)
|
||||
assert.Equal(d.expectedVersion, version, msg)
|
||||
assert.Equal(d.expectedDetails, details, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseURLIsValid(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
url string
|
||||
expectError bool
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{"", true},
|
||||
{"foo", true},
|
||||
{"foo bar", true},
|
||||
{"https://google.com", true},
|
||||
{projectAPIURL, true},
|
||||
|
||||
{kataLegacyReleaseURL, false},
|
||||
{kataReleaseURL, false},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
err := releaseURLIsValid(d.url)
|
||||
msg = fmt.Sprintf("%s, error: %v", msg, err)
|
||||
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
} else {
|
||||
assert.NoError(err, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadURLIsValid(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
url string
|
||||
expectError bool
|
||||
}
|
||||
|
||||
validKata1xDownload := "https://github.com/kata-containers/runtime/releases/download/1.12.0-alpha1/kata-static-1.12.0-alpha1-x86_64.tar.xz"
|
||||
validKata2xDownload := "https://github.com/kata-containers/kata-containers/releases/download/2.0.0-alpha3/kata-static-2.0.0-alpha3-x86_64.tar.xz"
|
||||
|
||||
data := []testData{
|
||||
{"", true},
|
||||
{"foo", true},
|
||||
{"foo bar", true},
|
||||
{"https://google.com", true},
|
||||
{projectURL, true},
|
||||
{validKata1xDownload, false},
|
||||
{validKata2xDownload, false},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
err := downloadURLIsValid(d.url)
|
||||
msg = fmt.Sprintf("%s, error: %v", msg, err)
|
||||
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
} else {
|
||||
assert.NoError(err, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreRelease(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
details releaseDetails
|
||||
includeAll bool
|
||||
expectIgnore bool
|
||||
}
|
||||
|
||||
verWithoutPreRelease, err := semver.Make("2.0.0")
|
||||
assert.NoError(err)
|
||||
|
||||
verWithPreRelease, err := semver.Make("2.0.0-alpha3")
|
||||
assert.NoError(err)
|
||||
|
||||
relWithoutPreRelease := releaseDetails{
|
||||
version: verWithoutPreRelease,
|
||||
}
|
||||
|
||||
relWithPreRelease := releaseDetails{
|
||||
version: verWithPreRelease,
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{relWithoutPreRelease, false, false},
|
||||
{relWithoutPreRelease, true, false},
|
||||
{relWithPreRelease, false, true},
|
||||
{relWithPreRelease, true, false},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
ignore := ignoreRelease(d.details, d.includeAll)
|
||||
|
||||
if d.expectIgnore {
|
||||
assert.True(ignore, msg)
|
||||
} else {
|
||||
assert.False(ignore, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReleases(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
url := "foo"
|
||||
expectedErrMsg := "unsupported protocol scheme"
|
||||
|
||||
for _, includeAll := range []bool{true, false} {
|
||||
euid := os.Geteuid()
|
||||
|
||||
msg := fmt.Sprintf("includeAll: %v, euid: %v", includeAll, euid)
|
||||
|
||||
_, _, err := getReleases(url, includeAll)
|
||||
msg = fmt.Sprintf("%s, error: %v", msg, err)
|
||||
|
||||
assert.Error(err, msg)
|
||||
|
||||
if euid == 0 {
|
||||
assert.Equal(err.Error(), errNoNetChecksAsRoot, msg)
|
||||
} else {
|
||||
assert.True(strings.Contains(err.Error(), expectedErrMsg), msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindNewestRelease(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
currentVer semver.Version
|
||||
versions []semver.Version
|
||||
expectAvailable bool
|
||||
expectVersion semver.Version
|
||||
expectError bool
|
||||
}
|
||||
|
||||
ver1, err := semver.Make("1.11.1")
|
||||
assert.NoError(err)
|
||||
|
||||
ver2, err := semver.Make("1.11.3")
|
||||
assert.NoError(err)
|
||||
|
||||
ver3, err := semver.Make("2.0.0")
|
||||
assert.NoError(err)
|
||||
|
||||
data := []testData{
|
||||
{semver.Version{}, []semver.Version{}, false, semver.Version{}, true},
|
||||
{ver1, []semver.Version{}, false, semver.Version{}, true},
|
||||
{ver1, []semver.Version{ver1}, false, semver.Version{}, false},
|
||||
{ver2, []semver.Version{ver1}, false, semver.Version{}, false},
|
||||
{ver1, []semver.Version{ver2}, true, ver2, false},
|
||||
{ver1, []semver.Version{ver3}, true, ver3, false},
|
||||
{ver1, []semver.Version{ver2, ver3}, true, ver3, false},
|
||||
{ver2, []semver.Version{ver1, ver3}, true, ver3, false},
|
||||
{ver2, []semver.Version{ver1}, false, semver.Version{}, false},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
available, version, err := findNewestRelease(d.currentVer, d.versions)
|
||||
msg = fmt.Sprintf("%s, available: %v, version: %v, error: %v", msg, available, version, err)
|
||||
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(err, msg)
|
||||
|
||||
if !d.expectAvailable {
|
||||
assert.False(available, msg)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Equal(d.expectVersion, version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNewReleaseType(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type testData struct {
|
||||
currentVer string
|
||||
latestVer string
|
||||
expectError bool
|
||||
result string
|
||||
}
|
||||
|
||||
data := []testData{
|
||||
{"2.0.0-alpha3", "1.0.0", true, ""},
|
||||
{"1.0.0", "1.0.0", true, ""},
|
||||
{"2.0.0", "1.0.0", true, ""},
|
||||
|
||||
{"1.0.0", "2.0.0", false, "major"},
|
||||
{"2.0.0-alpha3", "2.0.0-alpha4", false, "pre-release"},
|
||||
{"1.0.0", "2.0.0-alpha3", false, "major pre-release"},
|
||||
|
||||
{"1.0.0", "1.1.2", false, "minor"},
|
||||
{"1.0.0", "1.1.2-pre2", false, "minor pre-release"},
|
||||
{"1.0.0", "1.1.2-foo", false, "minor pre-release"},
|
||||
|
||||
{"1.0.0", "1.0.3", false, "patch"},
|
||||
{"1.0.0-beta29", "1.0.0-beta30", false, "pre-release"},
|
||||
{"1.0.0", "1.0.3-alpha99.1b", false, "patch pre-release"},
|
||||
}
|
||||
|
||||
for i, d := range data {
|
||||
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
||||
|
||||
current, err := semver.Make(d.currentVer)
|
||||
msg = fmt.Sprintf("%s, current: %v, error: %v", msg, current, err)
|
||||
|
||||
assert.NoError(err, msg)
|
||||
|
||||
latest, err := semver.Make(d.latestVer)
|
||||
assert.NoError(err, msg)
|
||||
|
||||
desc, err := getNewReleaseType(current, latest)
|
||||
if d.expectError {
|
||||
assert.Error(err, msg)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.NoError(err, msg)
|
||||
assert.Equal(d.result, desc, msg)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user