From fa53d193141686244ea8802368fb11576aebdbbc Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Mon, 12 Jul 2021 14:01:47 +0530 Subject: [PATCH] Added version flag in sandbox (#137) * Added version flag in the sandbox Signed-off-by: Yuvraj --- .../config/subcommand/sandbox/config_flags.go | 1 + .../subcommand/sandbox/config_flags_test.go | 14 +++ .../subcommand/sandbox/sandbox_config.go | 3 +- flytectl/cmd/sandbox/start.go | 76 +++++++++--- flytectl/cmd/sandbox/start_test.go | 108 +++++++++++++++--- flytectl/cmd/version/version.go | 39 +++---- flytectl/cmd/version/version_test.go | 29 ++--- flytectl/pkg/util/util.go | 32 +++++- flytectl/pkg/util/util_test.go | 34 ++++++ 9 files changed, 261 insertions(+), 75 deletions(-) diff --git a/flytectl/cmd/config/subcommand/sandbox/config_flags.go b/flytectl/cmd/config/subcommand/sandbox/config_flags.go index b727f8a04dc..7126d6a2e3c 100755 --- a/flytectl/cmd/config/subcommand/sandbox/config_flags.go +++ b/flytectl/cmd/config/subcommand/sandbox/config_flags.go @@ -51,5 +51,6 @@ func (Config) mustMarshalJSON(v json.Marshaler) string { func (cfg Config) GetPFlagSet(prefix string) *pflag.FlagSet { cmdFlags := pflag.NewFlagSet("Config", pflag.ExitOnError) cmdFlags.StringVar(&DefaultConfig.Source, fmt.Sprintf("%v%v", prefix, "source"), DefaultConfig.Source, " Path of your source code") + cmdFlags.StringVar(&DefaultConfig.Version, fmt.Sprintf("%v%v", prefix, "version"), DefaultConfig.Version, "Version of flyte") return cmdFlags } diff --git a/flytectl/cmd/config/subcommand/sandbox/config_flags_test.go b/flytectl/cmd/config/subcommand/sandbox/config_flags_test.go index 09d41aa631b..f01337ec13b 100755 --- a/flytectl/cmd/config/subcommand/sandbox/config_flags_test.go +++ b/flytectl/cmd/config/subcommand/sandbox/config_flags_test.go @@ -113,4 +113,18 @@ func TestConfig_SetFlags(t *testing.T) { } }) }) + t.Run("Test_version", func(t *testing.T) { + + t.Run("Override", func(t *testing.T) { + testValue := "1" + + cmdFlags.Set("version", testValue) + if vString, err := cmdFlags.GetString("version"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.Version) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) } diff --git a/flytectl/cmd/config/subcommand/sandbox/sandbox_config.go b/flytectl/cmd/config/subcommand/sandbox/sandbox_config.go index 833a950495a..5d3bfa91845 100644 --- a/flytectl/cmd/config/subcommand/sandbox/sandbox_config.go +++ b/flytectl/cmd/config/subcommand/sandbox/sandbox_config.go @@ -7,5 +7,6 @@ var ( //Config type Config struct { - Source string `json:"source" pflag:", Path of your source code"` + Source string `json:"source" pflag:",Path of your source code"` + Version string `json:"version" pflag:",Version of flyte"` } diff --git a/flytectl/cmd/sandbox/start.go b/flytectl/cmd/sandbox/start.go index 5853ee50605..a1de9e2f6f8 100644 --- a/flytectl/cmd/sandbox/start.go +++ b/flytectl/cmd/sandbox/start.go @@ -8,6 +8,9 @@ import ( "os" "path/filepath" + f "github.com/flyteorg/flytectl/pkg/filesystemutils" + "github.com/flyteorg/flytectl/pkg/util" + "github.com/flyteorg/flytectl/pkg/docker" "github.com/docker/docker/api/types/mount" @@ -30,9 +33,22 @@ Mount your source code repository inside sandbox :: bin/flytectl sandbox start --source=$HOME/flyteorg/flytesnacks + +Run specific version of flyte, Only available after v0.14.0+ +:: + + bin/flytectl sandbox start --version=v0.14.0 Usage ` + GeneratedManifest = "/flyteorg/share/flyte_generated.yaml" + FlyteReleaseURL = "/flyteorg/flyte/releases/download/%v/flyte_sandbox_manifest.yaml" + FlyteMinimumVersionSupported = "v0.14.0" + GithubURL = "https://github.com" +) + +var ( + FlyteManifest = f.FilePathJoin(f.UserHomeDir(), ".flyte", "flyte_generated.yaml") ) type ExecResult struct { @@ -57,6 +73,10 @@ func startSandboxCluster(ctx context.Context, args []string, cmdCtx cmdCore.Comm func startSandbox(ctx context.Context, cli docker.Docker, reader io.Reader) (*bufio.Scanner, error) { fmt.Printf("%v Bootstrapping a brand new flyte cluster... %v %v\n", emoji.FactoryWorker, emoji.Hammer, emoji.Wrench) + if err := docker.RemoveSandbox(ctx, cli, reader); err != nil { + return nil, err + } + if err := docker.SetupFlyteDir(); err != nil { return nil, err } @@ -65,25 +85,15 @@ func startSandbox(ctx context.Context, cli docker.Docker, reader io.Reader) (*bu return nil, err } - if err := docker.RemoveSandbox(ctx, cli, reader); err != nil { + if err := mountSourceCode(); err != nil { return nil, err } - if len(sandboxConfig.DefaultConfig.Source) > 0 { - source, err := filepath.Abs(sandboxConfig.DefaultConfig.Source) - if err != nil { - return nil, err - } - docker.Volumes = append(docker.Volumes, mount.Mount{ - Type: mount.TypeBind, - Source: source, - Target: docker.Source, - }) + if err := mountFlyteManifest(sandboxConfig.DefaultConfig.Version); err != nil { + return nil, err } fmt.Printf("%v pulling docker image %s\n", emoji.Whale, docker.ImageName) - os.Setenv("KUBECONFIG", docker.Kubeconfig) - os.Setenv("FLYTECTL_CONFIG", docker.FlytectlConfig) if err := docker.PullDockerImage(ctx, cli, docker.ImageName); err != nil { return nil, err } @@ -111,3 +121,43 @@ func startSandbox(ctx context.Context, cli docker.Docker, reader io.Reader) (*bu return logReader, nil } + +func mountSourceCode() error { + if len(sandboxConfig.DefaultConfig.Source) > 0 { + source, err := filepath.Abs(sandboxConfig.DefaultConfig.Source) + if err != nil { + return err + } + docker.Volumes = append(docker.Volumes, mount.Mount{ + Type: mount.TypeBind, + Source: source, + Target: docker.Source, + }) + } + return nil +} + +func mountFlyteManifest(version string) error { + if len(version) > 0 { + isGreater, err := util.IsVersionGreaterThan(version, FlyteMinimumVersionSupported) + if err != nil { + return err + } + if !isGreater { + return fmt.Errorf("version flag only support %s+ flyte version", FlyteMinimumVersionSupported) + } + response, err := util.GetRequest(GithubURL, fmt.Sprintf(FlyteReleaseURL, version)) + if err != nil { + return err + } + if err := util.WriteIntoFile(response, FlyteManifest); err != nil { + return err + } + docker.Volumes = append(docker.Volumes, mount.Mount{ + Type: mount.TypeBind, + Source: FlyteManifest, + Target: GeneratedManifest, + }) + } + return nil +} diff --git a/flytectl/cmd/sandbox/start_test.go b/flytectl/cmd/sandbox/start_test.go index 042860ed608..3e06516936a 100644 --- a/flytectl/cmd/sandbox/start_test.go +++ b/flytectl/cmd/sandbox/start_test.go @@ -63,7 +63,8 @@ func TestStartSandboxFunc(t *testing.T) { bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: sandboxConfig.DefaultConfig.Source, Target: docker.Source, @@ -101,7 +102,8 @@ func TestStartSandboxFunc(t *testing.T) { sandboxConfig.DefaultConfig.Source = "../" absPath, err := filepath.Abs(sandboxConfig.DefaultConfig.Source) assert.Nil(t, err) - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: absPath, Target: docker.Source, @@ -131,13 +133,90 @@ func TestStartSandboxFunc(t *testing.T) { _, err = startSandbox(ctx, mockDocker, os.Stdin) assert.Nil(t, err) }) + t.Run("Successfully run sandbox cluster with specific version", func(t *testing.T) { + ctx := context.Background() + errCh := make(chan error) + bodyStatus := make(chan container.ContainerWaitOKBody) + mockDocker := &mocks.Docker{} + sandboxConfig.DefaultConfig.Version = "v0.15.0" + sandboxConfig.DefaultConfig.Source = "" + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ + Type: mount.TypeBind, + Source: FlyteManifest, + Target: GeneratedManifest, + }) + mockDocker.OnContainerCreate(ctx, &container.Config{ + Env: docker.Environment, + Image: docker.ImageName, + Tty: false, + ExposedPorts: p1, + }, &container.HostConfig{ + Mounts: volumes, + PortBindings: p2, + Privileged: true, + }, nil, nil, mock.Anything).Return(container.ContainerCreateCreatedBody{ + ID: "Hello", + }, nil) + mockDocker.OnContainerStart(ctx, "Hello", types.ContainerStartOptions{}).Return(nil) + mockDocker.OnContainerList(ctx, types.ContainerListOptions{All: true}).Return([]types.Container{}, nil) + mockDocker.OnImagePullMatch(ctx, mock.Anything, types.ImagePullOptions{}).Return(os.Stdin, nil) + mockDocker.OnContainerLogsMatch(ctx, mock.Anything, types.ContainerLogsOptions{ + ShowStderr: true, + ShowStdout: true, + Timestamps: true, + Follow: true, + }).Return(nil, nil) + mockDocker.OnContainerWaitMatch(ctx, mock.Anything, container.WaitConditionNotRunning).Return(bodyStatus, errCh) + _, err := startSandbox(ctx, mockDocker, os.Stdin) + assert.Nil(t, err) + }) + t.Run("Failed run sandbox cluster with wrong version", func(t *testing.T) { + ctx := context.Background() + errCh := make(chan error) + bodyStatus := make(chan container.ContainerWaitOKBody) + mockDocker := &mocks.Docker{} + sandboxConfig.DefaultConfig.Version = "v0.13.0" + sandboxConfig.DefaultConfig.Source = "" + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ + Type: mount.TypeBind, + Source: FlyteManifest, + Target: GeneratedManifest, + }) + mockDocker.OnContainerCreate(ctx, &container.Config{ + Env: docker.Environment, + Image: docker.ImageName, + Tty: false, + ExposedPorts: p1, + }, &container.HostConfig{ + Mounts: volumes, + PortBindings: p2, + Privileged: true, + }, nil, nil, mock.Anything).Return(container.ContainerCreateCreatedBody{ + ID: "Hello", + }, nil) + mockDocker.OnContainerStart(ctx, "Hello", types.ContainerStartOptions{}).Return(nil) + mockDocker.OnContainerList(ctx, types.ContainerListOptions{All: true}).Return([]types.Container{}, nil) + mockDocker.OnImagePullMatch(ctx, mock.Anything, types.ImagePullOptions{}).Return(os.Stdin, nil) + mockDocker.OnContainerLogsMatch(ctx, mock.Anything, types.ContainerLogsOptions{ + ShowStderr: true, + ShowStdout: true, + Timestamps: true, + Follow: true, + }).Return(nil, nil) + mockDocker.OnContainerWaitMatch(ctx, mock.Anything, container.WaitConditionNotRunning).Return(bodyStatus, errCh) + _, err := startSandbox(ctx, mockDocker, os.Stdin) + assert.NotNil(t, err) + }) t.Run("Error in pulling image", func(t *testing.T) { ctx := context.Background() errCh := make(chan error) bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: sandboxConfig.DefaultConfig.Source, Target: docker.Source, @@ -173,7 +252,8 @@ func TestStartSandboxFunc(t *testing.T) { bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: sandboxConfig.DefaultConfig.Source, Target: docker.Source, @@ -216,19 +296,15 @@ func TestStartSandboxFunc(t *testing.T) { errCh := make(chan error) bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} - sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ - Type: mount.TypeBind, - Source: sandboxConfig.DefaultConfig.Source, - Target: docker.Source, - }) + sandboxConfig.DefaultConfig.Source = "" + sandboxConfig.DefaultConfig.Version = "" mockDocker.OnContainerCreate(ctx, &container.Config{ Env: docker.Environment, Image: docker.ImageName, Tty: false, ExposedPorts: p1, }, &container.HostConfig{ - Mounts: volumes, + Mounts: docker.Volumes, PortBindings: p2, Privileged: true, }, nil, nil, mock.Anything).Return(container.ContainerCreateCreatedBody{ @@ -247,13 +323,18 @@ func TestStartSandboxFunc(t *testing.T) { _, err := startSandbox(ctx, mockDocker, os.Stdin) assert.NotNil(t, err) }) + t.Run("Failed manifest", func(t *testing.T) { + err := mountFlyteManifest("v100.9.9") + assert.NotNil(t, err) + }) t.Run("Error in reading logs", func(t *testing.T) { ctx := context.Background() errCh := make(chan error) bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: sandboxConfig.DefaultConfig.Source, Target: docker.Source, @@ -289,7 +370,8 @@ func TestStartSandboxFunc(t *testing.T) { bodyStatus := make(chan container.ContainerWaitOKBody) mockDocker := &mocks.Docker{} sandboxConfig.DefaultConfig.Source = f.UserHomeDir() - volumes := append(docker.Volumes, mount.Mount{ + volumes := docker.Volumes + volumes = append(volumes, mount.Mount{ Type: mount.TypeBind, Source: sandboxConfig.DefaultConfig.Source, Target: docker.Source, diff --git a/flytectl/cmd/version/version.go b/flytectl/cmd/version/version.go index 4510c88aad2..a48c9c6f904 100644 --- a/flytectl/cmd/version/version.go +++ b/flytectl/cmd/version/version.go @@ -10,7 +10,6 @@ import ( "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/admin" "github.com/flyteorg/flytestdlib/logger" stdlibversion "github.com/flyteorg/flytestdlib/version" - hversion "github.com/hashicorp/go-version" "github.com/spf13/cobra" ) @@ -23,13 +22,15 @@ Example version. bin/flytectl version ` - latestVersionMessage = "Installed flytectl version is the latest" - upgradeVersionMessage = "A newer version of flytectl is available [%v] Please upgrade using - https://docs.flyte.org/projects/flytectl/en/latest/index.html" flytectlAppName = "flytectl" controlPlanAppName = "controlPlane" - flytectlReleasePath = "/repos/flyteorg/flytectl/releases/latest" + GithubAPIURL = "https://api.github.com" + latestVersionMessage = "Installed flytectl version is the latest" + upgradeVersionMessage = "A newer version of flytectl is available [%v] Please upgrade using - https://docs.flyte.org/projects/flytectl/en/latest/index.html" ) +var flytectlReleasePath = "/repos/flyteorg/flytectl/releases/latest" + type versionOutput struct { // Specifies the Name of app App string `json:"App,omitempty"` @@ -54,13 +55,19 @@ func GetVersionCommand(rootCmd *cobra.Command) map[string]cmdCore.CommandEntry { func getVersion(ctx context.Context, args []string, cmdCtx cmdCore.CommandContext) error { latest, err := getLatestVersion(flytectlReleasePath) if err != nil { - return err + logger.Errorf(ctx, "Get latest version of flyte got failed", err) } - message, err := compareVersion(latest, stdlibversion.Version) + isGreater, err := util.IsVersionGreaterThan(latest, stdlibversion.Version) if err != nil { - return err + logger.Errorf(ctx, "Error while comparing the flytectl version", err) } + + message := latestVersionMessage + if isGreater { + message = fmt.Sprintf(upgradeVersionMessage, latest) + } + fmt.Println(message) // Print Flytectl if err := printVersion(versionOutput{ @@ -87,22 +94,6 @@ func printVersion(response versionOutput) error { return nil } -func compareVersion(latest, current string) (string, error) { - semanticVersion, err := hversion.NewVersion(latest) - if err != nil { - return "", err - } - currentVersion, err := hversion.NewVersion(current) - if err != nil { - return "", err - } - if currentVersion.LessThan(semanticVersion) { - return fmt.Sprintf(upgradeVersionMessage, latest), nil - } - - return latestVersionMessage, nil -} - func getControlPlaneVersion(ctx context.Context, cmdCtx cmdCore.CommandContext) error { v, err := cmdCtx.AdminClient().GetVersion(ctx, &admin.GetVersionRequest{}) if err != nil || v == nil { @@ -122,7 +113,7 @@ func getControlPlaneVersion(ctx context.Context, cmdCtx cmdCore.CommandContext) } func getLatestVersion(path string) (string, error) { - response, err := util.GetRequest("https://api.github.com", path) + response, err := util.GetRequest(GithubAPIURL, path) if err != nil { return "", err } diff --git a/flytectl/cmd/version/version_test.go b/flytectl/cmd/version/version_test.go index 2366a3a8565..08681819b03 100644 --- a/flytectl/cmd/version/version_test.go +++ b/flytectl/cmd/version/version_test.go @@ -90,24 +90,6 @@ func TestVersionUtilFunc(t *testing.T) { assert.NotNil(t, err) assert.Equal(t, len(tag), 0) }) - t.Run("Compare flytectl version when upgrade available", func(t *testing.T) { - message, err := compareVersion("v1.1.21", testVersion) - assert.Nil(t, err) - assert.Equal(t, fmt.Sprintf(upgradeVersionMessage, "v1.1.21"), message) - }) - t.Run("Compare flytectl version", func(t *testing.T) { - message, err := compareVersion(testVersion, testVersion) - assert.Nil(t, err) - assert.Equal(t, latestVersionMessage, message) - }) - t.Run("Error in compare flytectl version", func(t *testing.T) { - _, err := compareVersion("vvvvvvvv", testVersion) - assert.NotNil(t, err) - }) - t.Run("Error in compare flytectl version", func(t *testing.T) { - _, err := compareVersion(testVersion, "vvvvvvvv") - assert.NotNil(t, err) - }) t.Run("Error in getting control plan version", func(t *testing.T) { ctx := context.Background() mockClient := new(mocks.AdminServiceClient) @@ -117,4 +99,15 @@ func TestVersionUtilFunc(t *testing.T) { err := getControlPlaneVersion(ctx, cmdCtx) assert.NotNil(t, err) }) + t.Run("Failed in getting version", func(t *testing.T) { + ctx := context.Background() + mockClient := new(mocks.AdminServiceClient) + mockOutStream := new(io.Writer) + flytectlReleasePath = "/release" + cmdCtx := cmdCore.NewCommandContext(mockClient, *mockOutStream) + mockClient.OnGetVersionMatch(ctx, &admin.GetVersionRequest{}).Return(nil, fmt.Errorf("error")) + err := getVersion(ctx, []string{}, cmdCtx) + assert.Nil(t, err) + }) + } diff --git a/flytectl/pkg/util/util.go b/flytectl/pkg/util/util.go index 6496f0fdd35..ee5a8e41847 100644 --- a/flytectl/pkg/util/util.go +++ b/flytectl/pkg/util/util.go @@ -5,6 +5,12 @@ import ( "fmt" "io/ioutil" "net/http" + + hversion "github.com/hashicorp/go-version" +) + +const ( + HTTPRequestErrorMessage = "something went wrong. Received status code [%v] while sending a request to [%s]" ) type githubversion struct { @@ -12,17 +18,19 @@ type githubversion struct { } func GetRequest(baseURL, url string) ([]byte, error) { - response, err := http.Get(fmt.Sprintf("%v%v", baseURL, url)) + response, err := http.Get(fmt.Sprintf("%s%s", baseURL, url)) if err != nil { return []byte(""), err } defer response.Body.Close() - - data, err := ioutil.ReadAll(response.Body) - if err != nil { - return []byte(""), err + if response.StatusCode == 200 { + data, err := ioutil.ReadAll(response.Body) + if err != nil { + return []byte(""), err + } + return data, nil } - return data, nil + return []byte(""), fmt.Errorf(HTTPRequestErrorMessage, response.StatusCode, fmt.Sprintf("%s%s", baseURL, url)) } func ParseGithubTag(data []byte) (string, error) { @@ -41,3 +49,15 @@ func WriteIntoFile(data []byte, file string) error { } return nil } + +func IsVersionGreaterThan(version1, version2 string) (bool, error) { + semanticVersion1, err := hversion.NewVersion(version1) + if err != nil { + return false, err + } + semanticVersion2, err := hversion.NewVersion(version2) + if err != nil { + return false, err + } + return semanticVersion2.LessThanOrEqual(semanticVersion1), nil +} diff --git a/flytectl/pkg/util/util_test.go b/flytectl/pkg/util/util_test.go index f4e07924289..ca9cd0f34f8 100644 --- a/flytectl/pkg/util/util_test.go +++ b/flytectl/pkg/util/util_test.go @@ -9,6 +9,7 @@ import ( const flytectlReleaseURL = "/repos/flyteorg/flytectl/releases/latest" const baseURL = "https://api.github.com" const wrongBaseURL = "htts://api.github.com" +const testVersion = "v0.1.20" func TestGetRequest(t *testing.T) { t.Run("Get request with 200", func(t *testing.T) { @@ -19,6 +20,10 @@ func TestGetRequest(t *testing.T) { _, err := GetRequest(wrongBaseURL, flytectlReleaseURL) assert.NotNil(t, err) }) + t.Run("Get request with 400", func(t *testing.T) { + _, err := GetRequest("https://github.com", "/flyteorg/flyte/releases/download/latest/flyte_eks_manifest.yaml") + assert.NotNil(t, err) + }) } func TestParseGithubTag(t *testing.T) { @@ -49,3 +54,32 @@ func TestWriteIntoFile(t *testing.T) { assert.NotNil(t, err) }) } + +func TestIsVersionGreaterThan(t *testing.T) { + t.Run("Compare flytectl version when upgrade available", func(t *testing.T) { + _, err := IsVersionGreaterThan("v1.1.21", testVersion) + assert.Nil(t, err) + }) + t.Run("Compare flytectl version greater then", func(t *testing.T) { + ok, err := IsVersionGreaterThan("v1.1.21", testVersion) + assert.Nil(t, err) + assert.Equal(t, true, ok) + }) + t.Run("Compare flytectl version smaller then", func(t *testing.T) { + ok, err := IsVersionGreaterThan("v0.1.19", testVersion) + assert.Nil(t, err) + assert.Equal(t, false, ok) + }) + t.Run("Compare flytectl version", func(t *testing.T) { + _, err := IsVersionGreaterThan(testVersion, testVersion) + assert.Nil(t, err) + }) + t.Run("Error in compare flytectl version", func(t *testing.T) { + _, err := IsVersionGreaterThan("vvvvvvvv", testVersion) + assert.NotNil(t, err) + }) + t.Run("Error in compare flytectl version", func(t *testing.T) { + _, err := IsVersionGreaterThan(testVersion, "vvvvvvvv") + assert.NotNil(t, err) + }) +}