mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #35436 from danwinship/utilversion
Automatic merge from submit-queue Add a package for handling version numbers (including non-"Semantic" versions) As noted in #32401, we are using Semantic Version-parsing libraries to parse version numbers that aren't necessarily "Semantic". Although, contrary to what I'd said there, it turns out that this wasn't actually currently a problem for the iptables code, because the regexp used to extract the version number out of the "iptables --version" output only pulled out three components, so given "iptables v1.4.19.1", it would have extracted just "1.4.19". Still, it could be a problem if they later release "1.5" rather than "1.5.0", or if we eventually need to _compare_ against a 4-digit version number. Also, as noted in #23854, we were also using two different semver libraries in different parts of the code (plus a wrapper around one of them in pkg/version). This PR adds pkg/util/version, with code to parse and compare both semver and non-semver version strings, and then updates kubernetes to use it everywhere (including getting rid of a bunch of code duplication in kubelet by making utilversion.Version implement the kubecontainer.Version interface directly). Ironically, this does not actually allow us to get rid of either of the vendored semver libraries, because we still have other dependencies that depend on each of them. (cadvisor uses blang/semver and etcd uses coreos/go-semver) fixes #32401, #23854
This commit is contained in:
commit
4505224cd3
@ -266,6 +266,7 @@ pkg/util/ratelimit
|
||||
pkg/util/replicaset
|
||||
pkg/util/restoptions
|
||||
pkg/util/validation/field
|
||||
pkg/util/version
|
||||
pkg/util/workqueue
|
||||
pkg/version/prometheus
|
||||
pkg/volume
|
||||
|
@ -52,8 +52,8 @@ go_library(
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/strategicpatch:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wsstream:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//pkg/watch/versioned:go_default_library",
|
||||
"//vendor:bitbucket.org/ww/goautoneg",
|
||||
|
@ -43,8 +43,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/flushwriter"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wsstream"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
@ -358,11 +358,11 @@ func isOldKubectl(userAgent string) bool {
|
||||
if len(subs) != 2 {
|
||||
return false
|
||||
}
|
||||
kubectlVersion, versionErr := version.Parse(subs[1])
|
||||
kubectlVersion, versionErr := utilversion.ParseSemantic(subs[1])
|
||||
if versionErr != nil {
|
||||
return false
|
||||
}
|
||||
return kubectlVersion.LT(version.MustParse("v1.5.0"))
|
||||
return kubectlVersion.LessThan(utilversion.MustParseSemantic("v1.5.0"))
|
||||
}
|
||||
|
||||
// TODO: Remove in 1.6. This is for backward compatibility with 1.4 kubectl.
|
||||
|
@ -42,8 +42,8 @@ go_library(
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/system:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/prometheus/client_golang/prometheus",
|
||||
],
|
||||
|
@ -33,7 +33,7 @@ import (
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/node"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@ -178,13 +178,13 @@ func (nc *NodeController) maybeDeleteTerminatingPod(obj interface{}) {
|
||||
// guarantee backwards compatibility of master API to kubelets with
|
||||
// versions less than 1.1.0
|
||||
node := nodeObj.(*v1.Node)
|
||||
v, err := version.Parse(node.Status.NodeInfo.KubeletVersion)
|
||||
v, err := utilversion.ParseSemantic(node.Status.NodeInfo.KubeletVersion)
|
||||
if err != nil {
|
||||
glog.V(0).Infof("couldn't parse verions %q of minion: %v", node.Status.NodeInfo.KubeletVersion, err)
|
||||
utilruntime.HandleError(nc.forcefullyDeletePod(pod))
|
||||
return
|
||||
}
|
||||
if gracefulDeletionVersion.GT(v) {
|
||||
if v.LessThan(gracefulDeletionVersion) {
|
||||
utilruntime.HandleError(nc.forcefullyDeletePod(pod))
|
||||
return
|
||||
}
|
||||
@ -239,12 +239,12 @@ func markAllPodsNotReady(kubeClient clientset.Interface, node *v1.Node) error {
|
||||
// Older versions were inflexible and modifying pod.Status directly through
|
||||
// the apiserver would result in unexpected outcomes.
|
||||
func nodeRunningOutdatedKubelet(node *v1.Node) bool {
|
||||
v, err := version.Parse(node.Status.NodeInfo.KubeletVersion)
|
||||
v, err := utilversion.ParseSemantic(node.Status.NodeInfo.KubeletVersion)
|
||||
if err != nil {
|
||||
glog.Errorf("couldn't parse version %q of node %v", node.Status.NodeInfo.KubeletVersion, err)
|
||||
return true
|
||||
}
|
||||
if podStatusReconciliationVersion.GT(v) {
|
||||
if v.LessThan(podStatusReconciliationVersion) {
|
||||
glog.Infof("Node %v running kubelet at (%v) which is less than the minimum version that allows nodecontroller to mark pods NotReady (%v).", node.Name, v, podStatusReconciliationVersion)
|
||||
return true
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ import (
|
||||
utilnode "k8s.io/kubernetes/pkg/util/node"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/system"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -52,11 +52,11 @@ func init() {
|
||||
|
||||
var (
|
||||
ErrCloudInstance = errors.New("cloud provider doesn't support instances.")
|
||||
gracefulDeletionVersion = version.MustParse("v1.1.0")
|
||||
gracefulDeletionVersion = utilversion.MustParseSemantic("v1.1.0")
|
||||
|
||||
// The minimum kubelet version for which the nodecontroller
|
||||
// can safely flip pod.Status to NotReady.
|
||||
podStatusReconciliationVersion = version.MustParse("v1.2.0")
|
||||
podStatusReconciliationVersion = utilversion.MustParseSemantic("v1.2.0")
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -36,8 +36,8 @@ go_library(
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups/fs",
|
||||
|
@ -29,7 +29,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/golang/glog"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
@ -47,6 +46,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
@ -66,7 +66,7 @@ const (
|
||||
|
||||
var (
|
||||
// The docker version in which containerd was introduced.
|
||||
containerdVersion = semver.MustParse("1.11.0")
|
||||
containerdVersion = utilversion.MustParseSemantic("1.11.0")
|
||||
)
|
||||
|
||||
// A non-user container tracked by the Kubelet.
|
||||
@ -632,10 +632,10 @@ func getPidsForProcess(name, pidFile string) ([]int, error) {
|
||||
// Temporarily export the function to be used by dockershim.
|
||||
// TODO(yujuhong): Move this function to dockershim once kubelet migrates to
|
||||
// dockershim as the default.
|
||||
func EnsureDockerInContainer(dockerVersion semver.Version, oomScoreAdj int, manager *fs.Manager) error {
|
||||
func EnsureDockerInContainer(dockerVersion *utilversion.Version, oomScoreAdj int, manager *fs.Manager) error {
|
||||
type process struct{ name, file string }
|
||||
dockerProcs := []process{{dockerProcessName, dockerPidFile}}
|
||||
if dockerVersion.GTE(containerdVersion) {
|
||||
if dockerVersion.AtLeast(containerdVersion) {
|
||||
dockerProcs = append(dockerProcs, process{containerdProcessName, containerdPidFile})
|
||||
}
|
||||
var errs []error
|
||||
@ -797,17 +797,16 @@ func isKernelPid(pid int) bool {
|
||||
}
|
||||
|
||||
// Helper for getting the docker version.
|
||||
func getDockerVersion(cadvisor cadvisor.Interface) semver.Version {
|
||||
var fallback semver.Version // Fallback to zero-value by default.
|
||||
func getDockerVersion(cadvisor cadvisor.Interface) *utilversion.Version {
|
||||
versions, err := cadvisor.VersionInfo()
|
||||
if err != nil {
|
||||
glog.Errorf("Error requesting cAdvisor VersionInfo: %v", err)
|
||||
return fallback
|
||||
return utilversion.MustParseSemantic("0.0.0")
|
||||
}
|
||||
dockerVersion, err := semver.Parse(versions.DockerVersion)
|
||||
dockerVersion, err := utilversion.ParseSemantic(versions.DockerVersion)
|
||||
if err != nil {
|
||||
glog.Errorf("Error parsing docker version %q: %v", versions.DockerVersion, err)
|
||||
return fallback
|
||||
return utilversion.MustParseSemantic("0.0.0")
|
||||
}
|
||||
return dockerVersion
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ go_library(
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/dockertools:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/cgroups/fs",
|
||||
"//vendor:github.com/opencontainers/runc/libcontainer/configs",
|
||||
|
@ -25,13 +25,13 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/golang/glog"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
)
|
||||
|
||||
@ -85,7 +85,7 @@ func (m *containerManager) doWork() {
|
||||
glog.Errorf("Unable to get docker version: %v", err)
|
||||
return
|
||||
}
|
||||
version, err := semver.Parse(v.Version)
|
||||
version, err := utilversion.ParseSemantic(v.Version)
|
||||
if err != nil {
|
||||
glog.Errorf("Unable to parse docker version %q: %v", v.Version, err)
|
||||
return
|
||||
|
@ -62,7 +62,7 @@ go_library(
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor:github.com/docker/distribution/digest",
|
||||
"//vendor:github.com/docker/distribution/reference",
|
||||
"//vendor:github.com/docker/docker/pkg/jsonmessage",
|
||||
|
@ -34,7 +34,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
dockercontainer "github.com/docker/engine-api/types/container"
|
||||
dockerstrslice "github.com/docker/engine-api/types/strslice"
|
||||
@ -73,6 +72,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/util/term"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -1033,37 +1033,9 @@ func getDockerNetworkMode(container *dockertypes.ContainerJSON) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// dockerVersion implements kubecontainer.Version interface by implementing
|
||||
// Compare() and String() (which is implemented by the underlying semver.Version)
|
||||
// TODO: this code is the same as rktVersion and may make sense to be moved to
|
||||
// somewhere shared.
|
||||
type dockerVersion struct {
|
||||
*semver.Version
|
||||
}
|
||||
|
||||
// newDockerVersion returns a semantically versioned docker version value
|
||||
func newDockerVersion(version string) (dockerVersion, error) {
|
||||
sem, err := semver.NewVersion(version)
|
||||
return dockerVersion{sem}, err
|
||||
}
|
||||
|
||||
func (r dockerVersion) String() string {
|
||||
return r.Version.String()
|
||||
}
|
||||
|
||||
func (r dockerVersion) Compare(other string) (int, error) {
|
||||
v, err := newDockerVersion(other)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if r.LessThan(*v.Version) {
|
||||
return -1, nil
|
||||
}
|
||||
if v.Version.LessThan(*r.Version) {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
func newDockerVersion(version string) (*utilversion.Version, error) {
|
||||
return utilversion.ParseSemantic(version)
|
||||
}
|
||||
|
||||
// apiVersion implements kubecontainer.Version interface by implementing
|
||||
|
@ -2117,12 +2117,14 @@ func TestDockerVersionComparison(t *testing.T) {
|
||||
{version: "1.10.4-rc1", compare: -1},
|
||||
{version: "1.11.1", compare: -1},
|
||||
{version: "1.11.1-rc4", compare: -1},
|
||||
{version: "invalid", compare: -1, err: true},
|
||||
{version: "invalid", err: true},
|
||||
} {
|
||||
testCase := fmt.Sprintf("test case #%d test version %q", i, test.version)
|
||||
res, err := v.Compare(test.version)
|
||||
assert.Equal(t, test.compare, res, testCase)
|
||||
assert.Equal(t, test.err, err != nil, testCase)
|
||||
if !test.err {
|
||||
assert.Equal(t, test.compare, res, testCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ go_library(
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/selinux:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor:github.com/docker/docker/pkg/jsonlog",
|
||||
"//vendor:github.com/fsnotify/fsnotify",
|
||||
"//vendor:github.com/golang/glog",
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/golang/glog"
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
|
||||
@ -43,6 +42,7 @@ import (
|
||||
kubetypes "k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/flowcontrol"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -202,33 +202,8 @@ func (m *kubeGenericRuntimeManager) Type() string {
|
||||
return m.runtimeName
|
||||
}
|
||||
|
||||
// runtimeVersion implements kubecontainer.Version interface by implementing
|
||||
// Compare() and String()
|
||||
type runtimeVersion struct {
|
||||
*semver.Version
|
||||
}
|
||||
|
||||
func newRuntimeVersion(version string) (runtimeVersion, error) {
|
||||
sem, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return runtimeVersion{}, err
|
||||
}
|
||||
return runtimeVersion{sem}, nil
|
||||
}
|
||||
|
||||
func (r runtimeVersion) Compare(other string) (int, error) {
|
||||
v, err := semver.NewVersion(other)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if r.LessThan(*v) {
|
||||
return -1, nil
|
||||
}
|
||||
if v.LessThan(*r.Version) {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
func newRuntimeVersion(version string) (*utilversion.Version, error) {
|
||||
return utilversion.ParseSemantic(version)
|
||||
}
|
||||
|
||||
func (m *kubeGenericRuntimeManager) getTypedVersion() (*runtimeapi.VersionResponse, error) {
|
||||
|
@ -47,10 +47,10 @@ go_library(
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/util/term:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/appc/spec/schema",
|
||||
"//vendor:github.com/appc/spec/schema/types",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//vendor:github.com/coreos/go-systemd/dbus",
|
||||
"//vendor:github.com/coreos/go-systemd/unit",
|
||||
"//vendor:github.com/coreos/rkt/api/v1alpha",
|
||||
|
@ -20,45 +20,21 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
rktapi "github.com/coreos/rkt/api/v1alpha"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
type versions struct {
|
||||
sync.RWMutex
|
||||
binVersion rktVersion
|
||||
apiVersion rktVersion
|
||||
binVersion *utilversion.Version
|
||||
apiVersion *utilversion.Version
|
||||
systemdVersion systemdVersion
|
||||
}
|
||||
|
||||
// rktVersion implementes kubecontainer.Version interface by implementing
|
||||
// Compare() and String() (which is implemented by the underlying semver.Version)
|
||||
type rktVersion struct {
|
||||
*semver.Version
|
||||
}
|
||||
|
||||
func newRktVersion(version string) (rktVersion, error) {
|
||||
sem, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return rktVersion{}, err
|
||||
}
|
||||
return rktVersion{sem}, nil
|
||||
}
|
||||
|
||||
func (r rktVersion) Compare(other string) (int, error) {
|
||||
v, err := semver.NewVersion(other)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
if r.LessThan(*v) {
|
||||
return -1, nil
|
||||
}
|
||||
if v.LessThan(*r.Version) {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
func newRktVersion(version string) (*utilversion.Version, error) {
|
||||
return utilversion.ParseSemantic(version)
|
||||
}
|
||||
|
||||
func (r *Runtime) getVersions() error {
|
||||
|
@ -25,7 +25,7 @@ go_library(
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor:github.com/davecgh/go-spew/spew",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
|
@ -32,7 +32,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
@ -47,6 +46,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -93,20 +93,19 @@ type KernelCompatTester interface {
|
||||
// an error if it fails to get the iptables version without error, in which
|
||||
// case it will also return false.
|
||||
func CanUseIPTablesProxier(iptver IPTablesVersioner, kcompat KernelCompatTester) (bool, error) {
|
||||
minVersion, err := semver.NewVersion(iptablesMinVersion)
|
||||
minVersion, err := utilversion.ParseGeneric(iptablesMinVersion)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// returns "X.Y.Z"
|
||||
versionString, err := iptver.GetVersion()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
version, err := semver.NewVersion(versionString)
|
||||
version, err := utilversion.ParseGeneric(versionString)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if version.LessThan(*minVersion) {
|
||||
if version.LessThan(minVersion) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ go_library(
|
||||
"//pkg/storage/storagebackend/factory:go_default_library",
|
||||
"//pkg/util/runtime:go_default_library",
|
||||
"//pkg/util/validation/field:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:golang.org/x/net/context",
|
||||
|
@ -39,7 +39,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/storage"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -235,11 +235,11 @@ func isOldKubectl(userAgent string) bool {
|
||||
if len(subs) != 2 {
|
||||
return false
|
||||
}
|
||||
kubectlVersion, versionErr := version.Parse(subs[1])
|
||||
kubectlVersion, versionErr := utilversion.ParseSemantic(subs[1])
|
||||
if versionErr != nil {
|
||||
return false
|
||||
}
|
||||
return kubectlVersion.LT(version.MustParse("v1.4.0"))
|
||||
return kubectlVersion.LessThan(utilversion.MustParseSemantic("v1.4.0"))
|
||||
}
|
||||
|
||||
// Create inserts a new item according to the unique key from the object.
|
||||
|
@ -20,7 +20,7 @@ go_library(
|
||||
"//pkg/util/dbus:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//vendor:github.com/coreos/go-semver/semver",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor:github.com/godbus/dbus",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
|
@ -23,12 +23,12 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
godbus "github.com/godbus/dbus"
|
||||
"github.com/golang/glog"
|
||||
utildbus "k8s.io/kubernetes/pkg/util/dbus"
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
type RulePosition string
|
||||
@ -40,7 +40,7 @@ const (
|
||||
|
||||
// An injectable interface for running iptables commands. Implementations must be goroutine-safe.
|
||||
type Interface interface {
|
||||
// GetVersion returns the "X.Y.Z" semver string for iptables.
|
||||
// GetVersion returns the "X.Y.Z" version string for iptables.
|
||||
GetVersion() (string, error)
|
||||
// EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true.
|
||||
EnsureChain(table Table, chain Chain) (bool, error)
|
||||
@ -462,45 +462,42 @@ func makeFullArgs(table Table, chain Chain, args ...string) []string {
|
||||
|
||||
// Checks if iptables has the "-C" flag
|
||||
func getIPTablesHasCheckCommand(vstring string) bool {
|
||||
minVersion, err := semver.NewVersion(MinCheckVersion)
|
||||
minVersion, err := utilversion.ParseGeneric(MinCheckVersion)
|
||||
if err != nil {
|
||||
glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err)
|
||||
return true
|
||||
}
|
||||
version, err := semver.NewVersion(vstring)
|
||||
version, err := utilversion.ParseGeneric(vstring)
|
||||
if err != nil {
|
||||
glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
|
||||
return true
|
||||
}
|
||||
if version.LessThan(*minVersion) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return version.AtLeast(minVersion)
|
||||
}
|
||||
|
||||
// Checks if iptables version has a "wait" flag
|
||||
func getIPTablesWaitFlag(vstring string) []string {
|
||||
version, err := semver.NewVersion(vstring)
|
||||
version, err := utilversion.ParseGeneric(vstring)
|
||||
if err != nil {
|
||||
glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
minVersion, err := semver.NewVersion(MinWaitVersion)
|
||||
minVersion, err := utilversion.ParseGeneric(MinWaitVersion)
|
||||
if err != nil {
|
||||
glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err)
|
||||
return nil
|
||||
}
|
||||
if version.LessThan(*minVersion) {
|
||||
if version.LessThan(minVersion) {
|
||||
return nil
|
||||
}
|
||||
|
||||
minVersion, err = semver.NewVersion(MinWait2Version)
|
||||
minVersion, err = utilversion.ParseGeneric(MinWait2Version)
|
||||
if err != nil {
|
||||
glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err)
|
||||
return nil
|
||||
}
|
||||
if version.LessThan(*minVersion) {
|
||||
if version.LessThan(minVersion) {
|
||||
return []string{"-w"}
|
||||
} else {
|
||||
return []string{"-w2"}
|
||||
@ -515,7 +512,7 @@ func getIPTablesVersionString(exec utilexec.Interface) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
versionMatcher := regexp.MustCompile("v([0-9]+\\.[0-9]+\\.[0-9]+)")
|
||||
versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
|
||||
match := versionMatcher.FindStringSubmatch(string(bytes))
|
||||
if match == nil {
|
||||
return "", fmt.Errorf("no iptables version found in string: %s", bytes)
|
||||
|
26
pkg/util/version/BUILD
Normal file
26
pkg/util/version/BUILD
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"version.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["version_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [],
|
||||
)
|
18
pkg/util/version/doc.go
Normal file
18
pkg/util/version/doc.go
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2016 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 version provides utilities for version number comparisons
|
||||
package version // import "k8s.io/kubernetes/pkg/util/version"
|
236
pkg/util/version/version.go
Normal file
236
pkg/util/version/version.go
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright 2016 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 version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is an opqaue representation of a version number
|
||||
type Version struct {
|
||||
components []uint
|
||||
semver bool
|
||||
preRelease string
|
||||
buildMetadata string
|
||||
}
|
||||
|
||||
var (
|
||||
// versionMatchRE splits a version string into numeric and "extra" parts
|
||||
versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
|
||||
// extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
|
||||
extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
|
||||
)
|
||||
|
||||
func parse(str string, semver bool) (*Version, error) {
|
||||
parts := versionMatchRE.FindStringSubmatch(str)
|
||||
if parts == nil {
|
||||
return nil, fmt.Errorf("could not parse %q as version", str)
|
||||
}
|
||||
numbers, extra := parts[1], parts[2]
|
||||
|
||||
components := strings.Split(numbers, ".")
|
||||
if (semver && len(components) != 3) || (!semver && len(components) < 2) {
|
||||
return nil, fmt.Errorf("illegal version string %q", str)
|
||||
}
|
||||
|
||||
v := &Version{
|
||||
components: make([]uint, len(components)),
|
||||
semver: semver,
|
||||
}
|
||||
for i, comp := range components {
|
||||
if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
num, err := strconv.ParseUint(comp, 10, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
|
||||
}
|
||||
v.components[i] = uint(num)
|
||||
}
|
||||
|
||||
if semver && extra != "" {
|
||||
extraParts := extraMatchRE.FindStringSubmatch(extra)
|
||||
if extraParts == nil {
|
||||
return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
|
||||
}
|
||||
v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
|
||||
|
||||
for _, comp := range strings.Split(v.preRelease, ".") {
|
||||
if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
|
||||
if strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ParseGeneric parses a "generic" version string. The version string must consist of two
|
||||
// or more dot-separated numeric fields (the first of which can't have leading zeroes),
|
||||
// followed by arbitrary uninterpreted data (which need not be separated from the final
|
||||
// numeric field by punctuation). For convenience, leading and trailing whitespace is
|
||||
// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
|
||||
func ParseGeneric(str string) (*Version, error) {
|
||||
return parse(str, false)
|
||||
}
|
||||
|
||||
// MustParseGeneric is like ParseGeneric except that it panics on error
|
||||
func MustParseGeneric(str string) *Version {
|
||||
v, err := ParseGeneric(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
|
||||
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
|
||||
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
|
||||
// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
|
||||
// ParseGeneric.
|
||||
func ParseSemantic(str string) (*Version, error) {
|
||||
return parse(str, true)
|
||||
}
|
||||
|
||||
// MustParseSemantic is like ParseSemantic except that it panics on error
|
||||
func MustParseSemantic(str string) *Version {
|
||||
v, err := ParseSemantic(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) BuildMetadata() string {
|
||||
return v.buildMetadata
|
||||
}
|
||||
|
||||
// String converts a Version back to a string; note that for versions parsed with
|
||||
// ParseGeneric, this will not include the trailing uninterpreted portion of the version
|
||||
// number.
|
||||
func (v *Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for i, comp := range v.components {
|
||||
if i > 0 {
|
||||
buffer.WriteString(".")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d", comp))
|
||||
}
|
||||
if v.preRelease != "" {
|
||||
buffer.WriteString("-")
|
||||
buffer.WriteString(v.preRelease)
|
||||
}
|
||||
if v.buildMetadata != "" {
|
||||
buffer.WriteString("+")
|
||||
buffer.WriteString(v.buildMetadata)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
|
||||
// if they are equal
|
||||
func (v *Version) compareInternal(other *Version) int {
|
||||
for i := range v.components {
|
||||
switch {
|
||||
case i >= len(other.components):
|
||||
if v.components[i] != 0 {
|
||||
return 1
|
||||
}
|
||||
case other.components[i] < v.components[i]:
|
||||
return 1
|
||||
case other.components[i] > v.components[i]:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
if !v.semver || !other.semver {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.preRelease == "" && other.preRelease != "":
|
||||
return 1
|
||||
case v.preRelease != "" && other.preRelease == "":
|
||||
return -1
|
||||
case v.preRelease == other.preRelease: // includes case where both are ""
|
||||
return 0
|
||||
}
|
||||
|
||||
vPR := strings.Split(v.preRelease, ".")
|
||||
oPR := strings.Split(other.preRelease, ".")
|
||||
for i := range vPR {
|
||||
if i >= len(oPR) {
|
||||
return 1
|
||||
}
|
||||
vNum, err := strconv.ParseUint(vPR[i], 10, 0)
|
||||
if err == nil {
|
||||
oNum, err := strconv.ParseUint(oPR[i], 10, 0)
|
||||
if err == nil {
|
||||
switch {
|
||||
case oNum < vNum:
|
||||
return 1
|
||||
case oNum > vNum:
|
||||
return -1
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if oPR[i] < vPR[i] {
|
||||
return 1
|
||||
} else if oPR[i] > vPR[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// AtLeast tests if a version is at least equal to a given minimum version. If both
|
||||
// Versions are Semantic Versions, this will use the Semantic Version comparison
|
||||
// algorithm. Otherwise, it will compare only the numeric components, with non-present
|
||||
// components being considered "0" (ie, "1.4" is equal to "1.4.0").
|
||||
func (v *Version) AtLeast(min *Version) bool {
|
||||
return v.compareInternal(min) != -1
|
||||
}
|
||||
|
||||
// LessThan tests if a version is less than a given version. (It is exactly the opposite
|
||||
// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
|
||||
// "is v new enough?".)
|
||||
func (v *Version) LessThan(other *Version) bool {
|
||||
return v.compareInternal(other) == -1
|
||||
}
|
||||
|
||||
// Compare compares v against a version string (which will be parsed as either Semantic
|
||||
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
|
||||
// it is greater than other, or 0 if they are equal.
|
||||
func (v *Version) Compare(other string) (int, error) {
|
||||
ov, err := parse(other, v.semver)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v.compareInternal(ov), nil
|
||||
}
|
259
pkg/util/version/version_test.go
Normal file
259
pkg/util/version/version_test.go
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
Copyright 2016 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 version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testItem struct {
|
||||
version string
|
||||
unparsed string
|
||||
equalsPrev bool
|
||||
}
|
||||
|
||||
func testOne(v *Version, item, prev testItem) error {
|
||||
str := v.String()
|
||||
if item.unparsed == "" {
|
||||
if str != item.version {
|
||||
return fmt.Errorf("bad round-trip: %q -> %q", item.version, str)
|
||||
}
|
||||
} else {
|
||||
if str != item.unparsed {
|
||||
return fmt.Errorf("bad unparse: %q -> %q, expected %q", item.version, str, item.unparsed)
|
||||
}
|
||||
}
|
||||
|
||||
if prev.version != "" {
|
||||
cmp, err := v.Compare(prev.version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected parse error: %v", err)
|
||||
}
|
||||
switch {
|
||||
case cmp == -1:
|
||||
return fmt.Errorf("unexpected ordering %q < %q", item.version, prev.version)
|
||||
case cmp == 0 && !item.equalsPrev:
|
||||
return fmt.Errorf("unexpected comparison %q == %q", item.version, item.version)
|
||||
case cmp == 1 && item.equalsPrev:
|
||||
return fmt.Errorf("unexpected comparison %q != %q", item.version, item.version)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSemanticVersions(t *testing.T) {
|
||||
tests := []testItem{
|
||||
// This is every version string that appears in the 2.0 semver spec,
|
||||
// sorted in strictly increasing order except as noted.
|
||||
{version: "0.1.0"},
|
||||
{version: "1.0.0-0.3.7"},
|
||||
{version: "1.0.0-alpha"},
|
||||
{version: "1.0.0-alpha+001", equalsPrev: true},
|
||||
{version: "1.0.0-alpha.1"},
|
||||
{version: "1.0.0-alpha.beta"},
|
||||
{version: "1.0.0-beta"},
|
||||
{version: "1.0.0-beta+exp.sha.5114f85", equalsPrev: true},
|
||||
{version: "1.0.0-beta.2"},
|
||||
{version: "1.0.0-beta.11"},
|
||||
{version: "1.0.0-rc.1"},
|
||||
{version: "1.0.0-x.7.z.92"},
|
||||
{version: "1.0.0"},
|
||||
{version: "1.0.0+20130313144700", equalsPrev: true},
|
||||
{version: "1.9.0"},
|
||||
{version: "1.10.0"},
|
||||
{version: "1.11.0"},
|
||||
{version: "2.0.0"},
|
||||
{version: "2.1.0"},
|
||||
{version: "2.1.1"},
|
||||
{version: "42.0.0"},
|
||||
|
||||
// We also allow whitespace and "v" prefix
|
||||
{version: " 42.0.0", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "\t42.0.0 ", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "43.0.0-1", unparsed: "43.0.0-1"},
|
||||
{version: "43.0.0-1 ", unparsed: "43.0.0-1", equalsPrev: true},
|
||||
{version: "v43.0.0-1", unparsed: "43.0.0-1", equalsPrev: true},
|
||||
{version: " v43.0.0", unparsed: "43.0.0"},
|
||||
{version: " 43.0.0 ", unparsed: "43.0.0", equalsPrev: true},
|
||||
}
|
||||
|
||||
var prev testItem
|
||||
for _, item := range tests {
|
||||
v, err := ParseSemantic(item.version)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected parse error: %v", err)
|
||||
continue
|
||||
}
|
||||
err = testOne(v, item, prev)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
prev = item
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadSemanticVersions(t *testing.T) {
|
||||
tests := []string{
|
||||
// "MUST take the form X.Y.Z"
|
||||
"1",
|
||||
"1.2",
|
||||
"1.2.3.4",
|
||||
".2.3",
|
||||
"1..3",
|
||||
"1.2.",
|
||||
"",
|
||||
"..",
|
||||
// "where X, Y, and Z are non-negative integers"
|
||||
"-1.2.3",
|
||||
"1.-2.3",
|
||||
"1.2.-3",
|
||||
"1a.2.3",
|
||||
"1.2a.3",
|
||||
"1.2.3a",
|
||||
"a1.2.3",
|
||||
"a.b.c",
|
||||
"1 .2.3",
|
||||
"1. 2.3",
|
||||
// "and MUST NOT contain leading zeroes."
|
||||
"01.2.3",
|
||||
"1.02.3",
|
||||
"1.2.03",
|
||||
// "[pre-release] identifiers MUST comprise only ASCII alphanumerics and hyphen"
|
||||
"1.2.3-/",
|
||||
// "[pre-release] identifiers MUST NOT be empty"
|
||||
"1.2.3-",
|
||||
"1.2.3-.",
|
||||
"1.2.3-foo.",
|
||||
"1.2.3-.foo",
|
||||
// "Numeric [pre-release] identifiers MUST NOT include leading zeroes"
|
||||
"1.2.3-01",
|
||||
// "[build metadata] identifiers MUST comprise only ASCII alphanumerics and hyphen"
|
||||
"1.2.3+/",
|
||||
// "[build metadata] identifiers MUST NOT be empty"
|
||||
"1.2.3+",
|
||||
"1.2.3+.",
|
||||
"1.2.3+foo.",
|
||||
"1.2.3+.foo",
|
||||
|
||||
// whitespace/"v"-prefix checks
|
||||
"v 1.2.3",
|
||||
"vv1.2.3",
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
_, err := ParseSemantic(tests[i])
|
||||
if err == nil {
|
||||
t.Errorf("unexpected success parsing invalid semver %q", tests[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericVersions(t *testing.T) {
|
||||
tests := []testItem{
|
||||
// This is all of the strings from TestSemanticVersions, plus some strings
|
||||
// from TestBadSemanticVersions that should parse as generic versions,
|
||||
// plus some additional strings.
|
||||
{version: "0.1.0", unparsed: "0.1.0"},
|
||||
{version: "1.0.0-0.3.7", unparsed: "1.0.0"},
|
||||
{version: "1.0.0-alpha", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0-alpha+001", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0-alpha.1", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0-alpha.beta", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0.beta", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0-beta+exp.sha.5114f85", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0.beta.2", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0.beta.11", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0.rc.1", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0-x.7.z.92", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.0.0+20130313144700", unparsed: "1.0.0", equalsPrev: true},
|
||||
{version: "1.2", unparsed: "1.2"},
|
||||
{version: "1.2a.3", unparsed: "1.2", equalsPrev: true},
|
||||
{version: "1.2.3", unparsed: "1.2.3"},
|
||||
{version: "1.2.3a", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3-foo.", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3-.foo", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3-01", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3+", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3+foo.", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3+.foo", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.02.3", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.03", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.003", unparsed: "1.2.3", equalsPrev: true},
|
||||
{version: "1.2.3.4", unparsed: "1.2.3.4"},
|
||||
{version: "1.2.3.4b3", unparsed: "1.2.3.4", equalsPrev: true},
|
||||
{version: "1.2.3.4.5", unparsed: "1.2.3.4.5"},
|
||||
{version: "1.9.0", unparsed: "1.9.0"},
|
||||
{version: "1.10.0", unparsed: "1.10.0"},
|
||||
{version: "1.11.0", unparsed: "1.11.0"},
|
||||
{version: "2.0.0", unparsed: "2.0.0"},
|
||||
{version: "2.1.0", unparsed: "2.1.0"},
|
||||
{version: "2.1.1", unparsed: "2.1.1"},
|
||||
{version: "42.0.0", unparsed: "42.0.0"},
|
||||
{version: " 42.0.0", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "\t42.0.0 ", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "42.0.0-1", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "42.0.0-1 ", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: "v42.0.0-1", unparsed: "42.0.0", equalsPrev: true},
|
||||
{version: " v43.0.0", unparsed: "43.0.0"},
|
||||
{version: " 43.0.0 ", unparsed: "43.0.0", equalsPrev: true},
|
||||
}
|
||||
|
||||
var prev testItem
|
||||
for _, item := range tests {
|
||||
v, err := ParseGeneric(item.version)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected parse error: %v", err)
|
||||
continue
|
||||
}
|
||||
err = testOne(v, item, prev)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
prev = item
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadGenericVersions(t *testing.T) {
|
||||
tests := []string{
|
||||
"1",
|
||||
"01.2.3",
|
||||
"-1.2.3",
|
||||
"1.-2.3",
|
||||
".2.3",
|
||||
"1..3",
|
||||
"1a.2.3",
|
||||
"a1.2.3",
|
||||
"1 .2.3",
|
||||
"1. 2.3",
|
||||
"1.bob",
|
||||
"bob",
|
||||
"v 1.2.3",
|
||||
"vv1.2.3",
|
||||
"",
|
||||
".",
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
_, err := ParseGeneric(tests[i])
|
||||
if err == nil {
|
||||
t.Errorf("unexpected success parsing invalid version %q", tests[i])
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ licenses(["notice"])
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
@ -13,20 +12,7 @@ go_library(
|
||||
srcs = [
|
||||
"base.go",
|
||||
"doc.go",
|
||||
"semver.go",
|
||||
"version.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["semver_test.go"],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [],
|
||||
)
|
||||
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 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 version
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func Parse(gitversion string) (semver.Version, error) {
|
||||
// optionally trim leading spaces then one v
|
||||
var seen bool
|
||||
gitversion = strings.TrimLeftFunc(gitversion, func(ch rune) bool {
|
||||
if seen {
|
||||
return false
|
||||
}
|
||||
if ch == 'v' {
|
||||
seen = true
|
||||
return true
|
||||
}
|
||||
return unicode.IsSpace(ch)
|
||||
})
|
||||
|
||||
return semver.Make(gitversion)
|
||||
}
|
||||
|
||||
func MustParse(gitversion string) semver.Version {
|
||||
v, err := Parse(gitversion)
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to parse semver from gitversion %q: %v", gitversion, err)
|
||||
}
|
||||
return v
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 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 version
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseVersion(t *testing.T) {
|
||||
cases := []struct {
|
||||
version string
|
||||
expectErr bool
|
||||
}{
|
||||
{version: "v1.0.1-alpha"},
|
||||
{version: "v0.19.3"},
|
||||
{version: "0.19.3"},
|
||||
{version: "v1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||
{version: "1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||
{version: "1.2.0-alpha.3.1264+0655e65b435106-dirty"},
|
||||
{version: "1.0.0"},
|
||||
{version: "\t v1.0.0"},
|
||||
{version: "vv1.0.0", expectErr: true},
|
||||
{version: "blah1.0.0", expectErr: true},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
_, err := Parse(c.version)
|
||||
if err != nil && !c.expectErr {
|
||||
t.Errorf("[%v]unexpected error: %v", i, err)
|
||||
}
|
||||
if err == nil && c.expectErr {
|
||||
t.Errorf("[%v]expected error for %s", i, c.version)
|
||||
}
|
||||
}
|
||||
}
|
@ -175,10 +175,10 @@ go_library(
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/util/system:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/util/workqueue:go_default_library",
|
||||
"//pkg/util/yaml:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
||||
|
@ -81,14 +81,13 @@ go_library(
|
||||
"//pkg/util/system:go_default_library",
|
||||
"//pkg/util/uuid:go_default_library",
|
||||
"//pkg/util/validation:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/watch:go_default_library",
|
||||
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
|
||||
"//plugin/pkg/scheduler/schedulercache:go_default_library",
|
||||
"//test/e2e/perftype:go_default_library",
|
||||
"//test/utils:go_default_library",
|
||||
"//vendor:github.com/blang/semver",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/google/cadvisor/info/v1",
|
||||
"//vendor:github.com/onsi/ginkgo",
|
||||
|
@ -40,7 +40,6 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/net/websocket"
|
||||
@ -87,8 +86,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/system"
|
||||
"k8s.io/kubernetes/pkg/util/uuid"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
||||
@ -234,8 +233,8 @@ func GetPauseImageNameForHostArch() string {
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.0 anymore, (tentatively
|
||||
// in v1.3).
|
||||
var SubResourcePodProxyVersion = version.MustParse("v1.1.0")
|
||||
var subResourceServiceAndNodeProxyVersion = version.MustParse("v1.2.0")
|
||||
var SubResourcePodProxyVersion = utilversion.MustParseSemantic("v1.1.0")
|
||||
var subResourceServiceAndNodeProxyVersion = utilversion.MustParseSemantic("v1.2.0")
|
||||
|
||||
func GetServicesProxyRequest(c clientset.Interface, request *restclient.Request) (*restclient.Request, error) {
|
||||
subResourceProxyAvailable, err := ServerVersionGTE(subResourceServiceAndNodeProxyVersion, c.Discovery())
|
||||
@ -350,7 +349,7 @@ func NodeOSDistroIs(supportedNodeOsDistros ...string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func SkipUnlessServerVersionGTE(v semver.Version, c discovery.ServerVersionInterface) {
|
||||
func SkipUnlessServerVersionGTE(v *utilversion.Version, c discovery.ServerVersionInterface) {
|
||||
gte, err := ServerVersionGTE(v, c)
|
||||
if err != nil {
|
||||
Failf("Failed to get server version: %v", err)
|
||||
@ -501,7 +500,7 @@ func WaitForPodsSuccess(c clientset.Interface, ns string, successPodLabels map[s
|
||||
return nil
|
||||
}
|
||||
|
||||
var ReadyReplicaVersion = version.MustParse("v1.4.0")
|
||||
var ReadyReplicaVersion = utilversion.MustParseSemantic("v1.4.0")
|
||||
|
||||
// WaitForPodsRunningReady waits up to timeout to ensure that all pods in
|
||||
// namespace ns are either running and ready, or failed but controlled by a
|
||||
@ -1624,19 +1623,19 @@ func (r podProxyResponseChecker) CheckAllResponses() (done bool, err error) {
|
||||
// version.
|
||||
//
|
||||
// TODO(18726): This should be incorporated into client.VersionInterface.
|
||||
func ServerVersionGTE(v semver.Version, c discovery.ServerVersionInterface) (bool, error) {
|
||||
func ServerVersionGTE(v *utilversion.Version, c discovery.ServerVersionInterface) (bool, error) {
|
||||
serverVersion, err := c.ServerVersion()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Unable to get server version: %v", err)
|
||||
}
|
||||
sv, err := version.Parse(serverVersion.GitVersion)
|
||||
sv, err := utilversion.ParseSemantic(serverVersion.GitVersion)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Unable to parse server version %q: %v", serverVersion.GitVersion, err)
|
||||
}
|
||||
return sv.GTE(v), nil
|
||||
return sv.AtLeast(v), nil
|
||||
}
|
||||
|
||||
func SkipUnlessKubectlVersionGTE(v semver.Version) {
|
||||
func SkipUnlessKubectlVersionGTE(v *utilversion.Version) {
|
||||
gte, err := KubectlVersionGTE(v)
|
||||
if err != nil {
|
||||
Failf("Failed to get kubectl version: %v", err)
|
||||
@ -1648,25 +1647,25 @@ func SkipUnlessKubectlVersionGTE(v semver.Version) {
|
||||
|
||||
// KubectlVersionGTE returns true if the kubectl version is greater than or
|
||||
// equal to v.
|
||||
func KubectlVersionGTE(v semver.Version) (bool, error) {
|
||||
func KubectlVersionGTE(v *utilversion.Version) (bool, error) {
|
||||
kv, err := KubectlVersion()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return kv.GTE(v), nil
|
||||
return kv.AtLeast(v), nil
|
||||
}
|
||||
|
||||
// KubectlVersion gets the version of kubectl that's currently being used (see
|
||||
// --kubectl-path in e2e.go to use an alternate kubectl).
|
||||
func KubectlVersion() (semver.Version, error) {
|
||||
func KubectlVersion() (*utilversion.Version, error) {
|
||||
output := RunKubectlOrDie("version", "--client")
|
||||
matches := gitVersionRegexp.FindStringSubmatch(output)
|
||||
if len(matches) != 2 {
|
||||
return semver.Version{}, fmt.Errorf("Could not find kubectl version in output %v", output)
|
||||
return nil, fmt.Errorf("Could not find kubectl version in output %v", output)
|
||||
}
|
||||
// Don't use the full match, as it contains "GitVersion:\"" and a
|
||||
// trailing "\"". Just use the submatch.
|
||||
return version.Parse(matches[1])
|
||||
return utilversion.ParseSemantic(matches[1])
|
||||
}
|
||||
|
||||
func PodsResponding(c clientset.Interface, ns, name string, wantName bool, pods *v1.PodList) error {
|
||||
|
@ -54,8 +54,8 @@ import (
|
||||
uexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/kubernetes/pkg/util/uuid"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
"k8s.io/kubernetes/test/e2e/generated"
|
||||
testutils "k8s.io/kubernetes/test/utils"
|
||||
@ -101,39 +101,39 @@ var (
|
||||
// that rely on extended pod logging options to work on clusters before that.
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.0 anymore, (tentatively in v1.3).
|
||||
extendedPodLogFilterVersion = version.MustParse("v1.1.0")
|
||||
extendedPodLogFilterVersion = utilversion.MustParseSemantic("v1.1.0")
|
||||
|
||||
// NodePorts were made optional in #12831 (v1.1.0) so we don't expect tests that used to
|
||||
// require NodePorts but no longer include them to work on clusters before that.
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.0 anymore, (tentatively in v1.3).
|
||||
nodePortsOptionalVersion = version.MustParse("v1.1.0")
|
||||
nodePortsOptionalVersion = utilversion.MustParseSemantic("v1.1.0")
|
||||
|
||||
// Jobs were introduced in v1.1, so we don't expect tests that rely on jobs to work on
|
||||
// clusters before that.
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.0 anymore, (tentatively in v1.3).
|
||||
jobsVersion = version.MustParse("v1.1.0")
|
||||
jobsVersion = utilversion.MustParseSemantic("v1.1.0")
|
||||
|
||||
// Deployments were introduced by default in v1.2, so we don't expect tests that rely on
|
||||
// deployments to work on clusters before that.
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.1 anymore, (tentatively in v1.4).
|
||||
deploymentsVersion = version.MustParse("v1.2.0-alpha.7.726")
|
||||
deploymentsVersion = utilversion.MustParseSemantic("v1.2.0-alpha.7.726")
|
||||
|
||||
// Pod probe parameters were introduced in #15967 (v1.2) so we don't expect tests that use
|
||||
// these probe parameters to work on clusters before that.
|
||||
//
|
||||
// TODO(ihmccreery): remove once we don't care about v1.1 anymore, (tentatively in v1.4).
|
||||
podProbeParametersVersion = version.MustParse("v1.2.0-alpha.4")
|
||||
podProbeParametersVersion = utilversion.MustParseSemantic("v1.2.0-alpha.4")
|
||||
|
||||
// 'kubectl create quota' was introduced in #28351 (v1.4) so we don't expect tests that use
|
||||
// 'kubectl create quota' to work on kubectl clients before that.
|
||||
kubectlCreateQuotaVersion = version.MustParse("v1.4.0-alpha.2")
|
||||
kubectlCreateQuotaVersion = utilversion.MustParseSemantic("v1.4.0-alpha.2")
|
||||
|
||||
// Returning container command exit codes in kubectl run/exec was introduced in #26541 (v1.4)
|
||||
// so we don't expect tests that verifies return code to work on kubectl clients before that.
|
||||
kubectlContainerExitCodeVersion = version.MustParse("v1.4.0-alpha.3")
|
||||
kubectlContainerExitCodeVersion = utilversion.MustParseSemantic("v1.4.0-alpha.3")
|
||||
)
|
||||
|
||||
// Stops everything from filePath from namespace ns and checks if everything matching selectors from the given namespace is correctly stopped.
|
||||
|
@ -29,8 +29,8 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
testutils "k8s.io/kubernetes/test/utils"
|
||||
|
||||
@ -44,7 +44,7 @@ const (
|
||||
// TODO support other ports besides 80
|
||||
var (
|
||||
portForwardRegexp = regexp.MustCompile("Forwarding from 127.0.0.1:([0-9]+) -> 80")
|
||||
portForwardPortToStdOutV = version.MustParse("v1.3.0-alpha.4")
|
||||
portForwardPortToStdOutV = utilversion.MustParseSemantic("v1.3.0-alpha.4")
|
||||
)
|
||||
|
||||
func pfPod(expectedClientData, chunks, chunkSize, chunkIntervalMillis string) *v1.Pod {
|
||||
|
@ -24,8 +24,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/util/uuid"
|
||||
utilversion "k8s.io/kubernetes/pkg/util/version"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
@ -33,7 +33,7 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var serviceAccountTokenNamespaceVersion = version.MustParse("v1.2.0")
|
||||
var serviceAccountTokenNamespaceVersion = utilversion.MustParseSemantic("v1.2.0")
|
||||
|
||||
var _ = framework.KubeDescribe("ServiceAccounts", func() {
|
||||
f := framework.NewDefaultFramework("svcaccounts")
|
||||
|
@ -872,11 +872,11 @@ k8s.io/kubernetes/pkg/util/testing,jlowdermilk,1
|
||||
k8s.io/kubernetes/pkg/util/threading,roberthbailey,1
|
||||
k8s.io/kubernetes/pkg/util/validation,Q-Lee,1
|
||||
k8s.io/kubernetes/pkg/util/validation/field,timstclair,1
|
||||
k8s.io/kubernetes/pkg/util/version,danwinship,0
|
||||
k8s.io/kubernetes/pkg/util/wait,Q-Lee,1
|
||||
k8s.io/kubernetes/pkg/util/workqueue,mtaufen,1
|
||||
k8s.io/kubernetes/pkg/util/wsstream,timothysc,1
|
||||
k8s.io/kubernetes/pkg/util/yaml,rmmh,1
|
||||
k8s.io/kubernetes/pkg/version,spxtr,1
|
||||
k8s.io/kubernetes/pkg/volume,saad-ali,0
|
||||
k8s.io/kubernetes/pkg/volume/aws_ebs,caesarxuchao,1
|
||||
k8s.io/kubernetes/pkg/volume/azure_dd,bgrant0607,1
|
||||
|
|
Loading…
Reference in New Issue
Block a user