Commit ecf21472 authored by Yang Guo's avatar Yang Guo

Support validating package versions in node conformance test

parent 22a6eedc
...@@ -15,12 +15,14 @@ go_library( ...@@ -15,12 +15,14 @@ go_library(
"docker_validator.go", "docker_validator.go",
"kernel_validator.go", "kernel_validator.go",
"os_validator.go", "os_validator.go",
"package_validator.go",
"report.go", "report.go",
"types.go", "types.go",
"validators.go", "validators.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/docker/engine-api/client:go_default_library", "//vendor/github.com/docker/engine-api/client:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library", "//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
...@@ -36,6 +38,7 @@ go_test( ...@@ -36,6 +38,7 @@ go_test(
"docker_validator_test.go", "docker_validator_test.go",
"kernel_validator_test.go", "kernel_validator_test.go",
"os_validator_test.go", "os_validator_test.go",
"package_validator_test.go",
], ],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
......
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package system
import (
"errors"
"fmt"
"reflect"
"testing"
)
func TestExtractUpstreamVersion(t *testing.T) {
for _, test := range []struct {
input string
expected string
}{
{
input: "",
expected: "",
},
{
input: "1.0.6",
expected: "1.0.6",
},
{
input: "1:1.0.6",
expected: "1.0.6",
},
{
input: "1.0.6-2ubuntu2.1",
expected: "1.0.6",
},
{
input: "1:1.0.6-2ubuntu2.1",
expected: "1.0.6",
},
} {
got := extractUpstreamVersion(test.input)
if test.expected != got {
t.Errorf("extractUpstreamVersion(%q) = %q, want %q", test.input, got, test.expected)
}
}
}
func TestToSemVer(t *testing.T) {
for _, test := range []struct {
input string
expected string
}{
{
input: "",
expected: "",
},
{
input: "1.2.3",
expected: "1.2.3",
},
{
input: "1.8.19p1",
expected: "1.8.19",
},
{
input: "1.8.19.p1",
expected: "1.8.19",
},
{
input: "p1",
expected: "",
},
{
input: "1.18",
expected: "1.18.0",
},
{
input: "481",
expected: "481.0.0",
},
{
input: "2.0.10.4",
expected: "2.0.10",
},
{
input: "03",
expected: "3.0.0",
},
{
input: "2.02",
expected: "2.2.0",
},
{
input: "8.0.0095",
expected: "8.0.95",
},
} {
got := toSemVer(test.input)
if test.expected != got {
t.Errorf("toSemVer(%q) = %q, want %q", test.input, got, test.expected)
}
}
}
func TestToSemVerRange(t *testing.T) {
for _, test := range []struct {
input string
expected string
}{
{
input: "",
expected: "",
},
{
input: ">=1.0.0",
expected: ">=1.0.0",
},
{
input: ">=1.0",
expected: ">=1.0.x",
},
{
input: ">=1",
expected: ">=1.x",
},
{
input: ">=1 || !2.3",
expected: ">=1.x || !2.3.x",
},
{
input: ">1 || >3.1.0 !4.2",
expected: ">1.x || >3.1.0 !4.2.x",
},
} {
got := toSemVerRange(test.input)
if test.expected != got {
t.Errorf("toSemVerRange(%q) = %q, want %q", test.input, got, test.expected)
}
}
}
// testPackageManager implements the packageManager interface.
type testPackageManager struct {
packageVersions map[string]string
}
func (m testPackageManager) getPackageVersion(packageName string) (string, error) {
if v, ok := m.packageVersions[packageName]; ok {
return v, nil
}
return "", fmt.Errorf("package %q does not exist", packageName)
}
func TestValidatePackageVersion(t *testing.T) {
testKernelRelease := "test-kernel-release"
manager := testPackageManager{
packageVersions: map[string]string{
"foo": "1.0.0",
"bar": "2.1.0",
"bar-" + testKernelRelease: "3.0.0",
},
}
v := &packageValidator{
reporter: DefaultReporter,
kernelRelease: testKernelRelease,
}
for _, test := range []struct {
desc string
specs []PackageSpec
err error
}{
{
desc: "all packages meet the spec",
specs: []PackageSpec{
{Name: "foo", VersionRange: ">=1.0"},
{Name: "bar", VersionRange: ">=2.0 <= 3.0"},
},
},
{
desc: "package version does not meet the spec",
specs: []PackageSpec{
{Name: "foo", VersionRange: ">=1.0"},
{Name: "bar", VersionRange: ">=3.0"},
},
err: errors.New("package \"bar 2.1.0\" does not meet the spec \"bar (>=3.0)\""),
},
{
desc: "package not installed",
specs: []PackageSpec{
{Name: "baz"},
},
err: errors.New("package \"baz\" does not exist"),
},
{
desc: "use variable in package name",
specs: []PackageSpec{
{Name: "bar-${KERNEL_RELEASE}", VersionRange: ">=3.0"},
},
},
} {
_, err := v.validate(test.specs, manager)
if test.err == nil && err != nil {
t.Errorf("%s: v.validate(): err = %s", test.desc, err)
}
if test.err != nil {
if err == nil {
t.Errorf("%s: v.validate() is expected to fail.", test.desc)
} else if test.err.Error() != err.Error() {
t.Errorf("%s: v.validate(): err = %q, want = %q", test.desc, err, test.err)
}
}
}
}
func TestApplyPackageOverride(t *testing.T) {
for _, test := range []struct {
overrides []PackageSpecOverride
osDistro string
specs []PackageSpec
expected []PackageSpec
}{
{
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
},
{
osDistro: "ubuntu",
overrides: []PackageSpecOverride{
{
OSDistro: "ubuntu",
Subtractions: []PackageSpec{{Name: "foo"}},
Additions: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}},
},
},
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
expected: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}},
},
{
osDistro: "ubuntu",
overrides: []PackageSpecOverride{
{
OSDistro: "debian",
Subtractions: []PackageSpec{{Name: "foo"}},
},
},
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
},
} {
got := applyPackageSpecOverride(test.specs, test.overrides, test.osDistro)
if !reflect.DeepEqual(test.expected, got) {
t.Errorf("applyPackageSpecOverride(%+v, %+v, %s) = %+v, want = %+v", test.specs, test.overrides, test.osDistro, got, test.expected)
}
}
}
...@@ -65,6 +65,41 @@ type RuntimeSpec struct { ...@@ -65,6 +65,41 @@ type RuntimeSpec struct {
*DockerSpec *DockerSpec
} }
// PackageSpec defines the required packages and their versions.
// PackageSpec is only supported on OS distro with Debian package manager.
//
// TODO(yguo0905): Support operator OR of multiple packages for the case where
// either "foo (>=1.0)" or "bar (>=2.0)" is required.
type PackageSpec struct {
// Name is the name of the package to be checked.
Name string
// VersionRange represents a range of versions that the package must
// satisfy. Note that the version requirement will not be enforced if
// the version range is empty. For example,
// - "" would match any versions but the package must be installed.
// - ">=1" would match "1.0.0", "1.0.1", "1.1.0", and "2.0".
// - ">1.0 <2.0" would match between both ranges, so "1.1.1" and "1.8.7"
// but not "1.0.0" or "2.0.0".
// - "<2.0.0 || >=3.0.0" would match "1.0.0" and "3.0.0" but not "2.0.0".
VersionRange string
// Description explains the reason behind this package requirements.
Description string
}
// PackageSpecOverride defines the overrides on the PackageSpec for an OS
// distro.
type PackageSpecOverride struct {
// OSDistro identifies to which OS distro this override applies.
// Must be "ubuntu", "cos" or "coreos".
OSDistro string
// Subtractions is a list of package names that are excluded from the
// package spec.
Subtractions []PackageSpec
// Additions is a list of additional package requirements included the
// package spec.
Additions []PackageSpec
}
// SysSpec defines the requirement of supported system. Currently, it only contains // SysSpec defines the requirement of supported system. Currently, it only contains
// spec for OS, Kernel and Cgroups. // spec for OS, Kernel and Cgroups.
type SysSpec struct { type SysSpec struct {
...@@ -76,6 +111,11 @@ type SysSpec struct { ...@@ -76,6 +111,11 @@ type SysSpec struct {
Cgroups []string Cgroups []string
// RuntimeSpec defines the spec for runtime. // RuntimeSpec defines the spec for runtime.
RuntimeSpec RuntimeSpec RuntimeSpec RuntimeSpec
// PackageSpec defines the required packages and their versions.
PackageSpecs []PackageSpec
// PackageSpec defines the overrides of the required packages and their
// versions for an OS distro.
PackageSpecOverrides []PackageSpecOverride
} }
// DefaultSysSpec is the default SysSpec. // DefaultSysSpec is the default SysSpec.
......
...@@ -56,6 +56,7 @@ func ValidateDefault(runtime string) (error, error) { ...@@ -56,6 +56,7 @@ func ValidateDefault(runtime string) (error, error) {
&OSValidator{Reporter: DefaultReporter}, &OSValidator{Reporter: DefaultReporter},
&KernelValidator{Reporter: DefaultReporter}, &KernelValidator{Reporter: DefaultReporter},
&CgroupsValidator{Reporter: DefaultReporter}, &CgroupsValidator{Reporter: DefaultReporter},
&packageValidator{reporter: DefaultReporter},
} }
// Docker-specific validators. // Docker-specific validators.
var dockerValidators = []Validator{ var dockerValidators = []Validator{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment