Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -64,6 +65,20 @@ func hidePersistentFlags(cmd *cobra.Command, flags ...string) {
}
}

// isVersionSkewParseError reports whether err came from
// "k8s.io/kubectl/pkg/cmd/version".getVersionSkewWarning failing to parse
// the client or server GitVersion as semver, rather than from a real
// connectivity/discovery failure. That check runs after the client/server
// versions have already been printed, so a malformed version string
// shouldn't be treated as fatal.
func isVersionSkewParseError(err error) bool {
if err == nil {
return false
}
msg := err.Error()
return strings.HasPrefix(msg, "client version error:") || strings.HasPrefix(msg, "server version error:")
}

func RootCmd() *cobra.Command {
// Resolve plugins directory early so ForwardCompletion can use it.
// Failures are non-fatal here — if we can't find the dir, completion
Expand Down Expand Up @@ -662,6 +677,31 @@ the server.`
return nil
}
versionCmd.GroupID = "other"
versionCmd.Run = nil
versionCmd.RunE = func(cmd *cobra.Command, args []string) error {
o := version.NewOptions(ioStreams)
o.ClientOnly, _ = cmd.Flags().GetBool("client")
o.Output, _ = cmd.Flags().GetString("output")

if err := o.Complete(factory, cmd, args); err != nil {
return err
}
if err := o.Validate(); err != nil {
return err
}

err := o.Run()
if isVersionSkewParseError(err) {
// Client/server versions were already printed above; the server
// (or, less commonly, the client binary itself) reported a
// version string that isn't valid semver, e.g. an unresolved
// `git archive` export-subst placeholder like "$Format:%H$".
// That shouldn't prevent `datumctl version` from succeeding.
fmt.Fprintf(ioStreams.ErrOut, "Warning: could not compare client/server versions: %s\n", err)
return nil
}
return err
}
rootCmd.AddCommand(versionCmd)

activityCmd := activity.NewActivityCommand(activity.ActivityCommandOptions{
Expand Down
35 changes: 35 additions & 0 deletions internal/cmd/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cmd

import (
"errors"
"fmt"
"testing"
)

func TestIsVersionSkewParseError(t *testing.T) {
tests := []struct {
name string
err error
want bool
}{
{"nil error", nil, false},
{
"unresolved git archive placeholder in server version",
fmt.Errorf("server version error: %w", errors.New(`could not parse pre-release/metadata (-master+$Format:%H$) in version "v0.0.0-master+$Format:%H$"`)),
true,
},
{
"unparseable client version",
fmt.Errorf("client version error: %w", errors.New(`could not parse "not-a-version" as version`)),
true,
},
{"unrelated error", errors.New("connection refused"), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isVersionSkewParseError(tt.err); got != tt.want {
t.Fatalf("isVersionSkewParseError(%v) = %v, want %v", tt.err, got, tt.want)
}
})
}
}