Kubectl: check version skew (#127365)
Signed-off-by: Omer Aplatony <omerap12@gmail.com> Kubernetes-commit: 35307319740a3a52cf4632c24b7f99d675537bdf
This commit is contained in:
parent
4afda566a9
commit
5ff92a69e3
|
@ -18,10 +18,10 @@ package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"math"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/version"
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// supportedMinorVersionSkew is the maximum supported difference between the client and server minor versions.
|
// supportedMinorVersionSkew is the maximum supported difference between the client and server minor versions.
|
||||||
|
@ -29,26 +29,26 @@ import (
|
||||||
// and server versions 1.18, 1.19, and 1.20 would be within the supported version skew for client version 1.19.
|
// and server versions 1.18, 1.19, and 1.20 would be within the supported version skew for client version 1.19.
|
||||||
const supportedMinorVersionSkew = 1
|
const supportedMinorVersionSkew = 1
|
||||||
|
|
||||||
// printVersionSkewWarning prints a warning message if the difference between the client and version is greater than
|
// getVersionSkewWarning returns a warning message if the difference between the client and version is greater than
|
||||||
// the supported version skew.
|
// the supported version skew.
|
||||||
func printVersionSkewWarning(w io.Writer, clientVersion, serverVersion apimachineryversion.Info) error {
|
func getVersionSkewWarning(clientVersion, serverVersion apimachineryversion.Info) (string, error) {
|
||||||
parsedClientVersion, err := version.ParseSemantic(clientVersion.GitVersion)
|
parsedClientVersion, err := version.ParseSemantic(clientVersion.GitVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", fmt.Errorf("client version error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedServerVersion, err := version.ParseSemantic(serverVersion.GitVersion)
|
parsedServerVersion, err := version.ParseSemantic(serverVersion.GitVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", fmt.Errorf("server version error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
majorVersionDifference := math.Abs(float64(parsedClientVersion.Major()) - float64(parsedServerVersion.Major()))
|
majorVersionDifference := math.Abs(float64(parsedClientVersion.Major()) - float64(parsedServerVersion.Major()))
|
||||||
minorVersionDifference := math.Abs(float64(parsedClientVersion.Minor()) - float64(parsedServerVersion.Minor()))
|
minorVersionDifference := math.Abs(float64(parsedClientVersion.Minor()) - float64(parsedServerVersion.Minor()))
|
||||||
|
|
||||||
if majorVersionDifference > 0 || minorVersionDifference > supportedMinorVersionSkew {
|
if majorVersionDifference > 0 || minorVersionDifference > supportedMinorVersionSkew {
|
||||||
fmt.Fprintf(w, "WARNING: version difference between client (%d.%d) and server (%d.%d) exceeds the supported minor version skew of +/-%d\n",
|
warningMessage := fmt.Sprintf("version difference between client (%d.%d) and server (%d.%d) exceeds the supported minor version skew of +/-%d",
|
||||||
parsedClientVersion.Major(), parsedClientVersion.Minor(), parsedServerVersion.Major(), parsedServerVersion.Minor(), supportedMinorVersionSkew)
|
parsedClientVersion.Major(), parsedClientVersion.Minor(), parsedServerVersion.Major(), parsedServerVersion.Minor(), supportedMinorVersionSkew)
|
||||||
|
return warningMessage, nil
|
||||||
}
|
}
|
||||||
|
return "", nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,12 @@ limitations under the License.
|
||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrintVersionSkewWarning(t *testing.T) {
|
func TestPrintVersionSkewWarning(t *testing.T) {
|
||||||
output := &bytes.Buffer{}
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
clientVersion apimachineryversion.Info
|
clientVersion apimachineryversion.Info
|
||||||
|
@ -82,14 +80,15 @@ func TestPrintVersionSkewWarning(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
output.Reset()
|
warningMessage, err := getVersionSkewWarning(tc.clientVersion, tc.serverVersion)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
printVersionSkewWarning(output, tc.clientVersion, tc.serverVersion)
|
if tc.isWarningExpected && warningMessage == "" {
|
||||||
|
t.Error("warning was expected")
|
||||||
if tc.isWarningExpected && output.Len() == 0 {
|
} else if !tc.isWarningExpected && warningMessage != "" {
|
||||||
t.Error("warning was expected, but not written to the output")
|
t.Errorf("warning was not expected. but got %s", warningMessage)
|
||||||
} else if !tc.isWarningExpected && output.Len() > 0 {
|
|
||||||
t.Errorf("warning was not expected, but was written to the output: %s", output.String())
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ var (
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ClientOnly bool
|
ClientOnly bool
|
||||||
Output string
|
Output string
|
||||||
|
WarningsAsErrors bool
|
||||||
|
|
||||||
args []string
|
args []string
|
||||||
|
|
||||||
|
@ -93,6 +94,9 @@ func NewCmdVersion(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cob
|
||||||
// Complete completes all the required options
|
// Complete completes all the required options
|
||||||
func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
o.WarningsAsErrors = cmd.Flags().Lookup("warnings-as-errors").Value.String() == "true"
|
||||||
|
|
||||||
if o.ClientOnly {
|
if o.ClientOnly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -162,11 +166,17 @@ func (o *Options) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionInfo.ServerVersion != nil {
|
if versionInfo.ServerVersion != nil {
|
||||||
if err := printVersionSkewWarning(o.ErrOut, *versionInfo.ClientVersion, *versionInfo.ServerVersion); err != nil {
|
warningMessage, err := getVersionSkewWarning(*versionInfo.ClientVersion, *versionInfo.ServerVersion)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if warningMessage != "" {
|
||||||
|
if o.WarningsAsErrors {
|
||||||
|
return errors.New(warningMessage)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(o.ErrOut, "Warning: %s\n", warningMessage) //nolint:errcheck
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverErr
|
return serverErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||||
|
|
||||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||||
|
@ -32,13 +30,18 @@ func TestNewCmdVersionClientVersion(t *testing.T) {
|
||||||
defer tf.Cleanup()
|
defer tf.Cleanup()
|
||||||
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||||
o := NewOptions(streams)
|
o := NewOptions(streams)
|
||||||
if err := o.Complete(tf, &cobra.Command{}, nil); err != nil {
|
|
||||||
|
cmd := NewCmdVersion(tf, streams)
|
||||||
|
cmd.Flags().Bool("warnings-as-errors", false, "")
|
||||||
|
|
||||||
|
if err := o.Complete(tf, cmd, nil); err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if err := o.Validate(); err != nil {
|
if err := o.Validate(); err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if err := o.Complete(tf, &cobra.Command{}, []string{"extraParameter0"}); err != nil {
|
|
||||||
|
if err := o.Complete(tf, cmd, []string{"extraParameter0"}); err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if err := o.Validate(); !strings.Contains(err.Error(), "extra arguments") {
|
if err := o.Validate(); !strings.Contains(err.Error(), "extra arguments") {
|
||||||
|
|
Loading…
Reference in New Issue