diff --git a/pkg/options/types.go b/pkg/options/types.go index 3b14497..25a5256 100644 --- a/pkg/options/types.go +++ b/pkg/options/types.go @@ -22,7 +22,8 @@ type MajorMinorPatchVersion struct { } type RawVersion struct { - Raw string + Sign string + Raw string } type VersionSpec interface { @@ -30,10 +31,6 @@ type VersionSpec interface { String() string } -func (v MajorMinorPatchVersion) String() string { - return fmt.Sprintf("%s%v.%v.%v", v.Sign, v.Major, v.Minor, v.Patch) -} - func compareMajorMinorPatch(sign string, nodeVersion, userVersion [3]int) bool { res := 0 for i := 0; i < 3; i++ { @@ -59,6 +56,16 @@ func compareMajorMinorPatch(sign string, nodeVersion, userVersion [3]int) bool { return false } +func compareRaw(sign string, nodeVersion, userVersion string) bool { + switch sign { + case "==": + return nodeVersion == userVersion + case "!=": + return nodeVersion != userVersion + } + return false +} + func tryParseWith(reString, version string) (int, int, int, bool) { re := regexp.MustCompile(reString) matches := re.FindStringSubmatch(version) @@ -100,10 +107,18 @@ func (v MajorMinorPatchVersion) Satisfies(otherVersion string) (bool, error) { ), nil } +func (v MajorMinorPatchVersion) String() string { + return fmt.Sprintf("%s%v.%v.%v", v.Sign, v.Major, v.Minor, v.Patch) +} + func (v RawVersion) Satisfies(otherVersion string) (bool, error) { - return v.Raw == otherVersion, nil + return compareRaw( + v.Sign, + otherVersion, + v.Raw, + ), nil } func (v RawVersion) String() string { - return v.Raw + return fmt.Sprintf("%s%v", v.Sign, v.Raw) } diff --git a/pkg/rolling/options.go b/pkg/rolling/options.go index 4ad7049..0d3a66e 100644 --- a/pkg/rolling/options.go +++ b/pkg/rolling/options.go @@ -29,7 +29,7 @@ var ( majorMinorPatchPattern = `^(>|<|!=|~=)(\d+|\*)\.(\d+|\*)\.(\d+|\*)$` majorMinorPatchRegexp = regexp.MustCompile(majorMinorPatchPattern) - rawPattern = `^==(.*)$` + rawPattern = `^(==|!=)(.*)$` rawRegexp = regexp.MustCompile(rawPattern) ) @@ -238,11 +238,12 @@ func parseVersionFlag(versionUnparsedFlag string) (options.VersionSpec, error) { } matches = rawRegexp.FindStringSubmatch(versionUnparsedFlag) - if len(matches) == 2 { + if len(matches) == 3 { // `--version` value is an arbitrary string value, and will // be compared directly return &options.RawVersion{ - Raw: matches[1], + Sign: matches[1], + Raw: matches[2], }, nil } diff --git a/tests/rolling_test.go b/tests/rolling_test.go index 95d8410..d1deba5 100644 --- a/tests/rolling_test.go +++ b/tests/rolling_test.go @@ -441,6 +441,80 @@ var _ = Describe("Test Rolling", func() { }, }, ), + Entry("filter nodes by --version flag, !=full_version_string_1234", TestCase{ + nodeConfiguration: [][]uint32{ + {1, 2, 3}, + }, + nodeInfoMap: map[uint32]mock.TestNodeInfo{ + 1: { + Version: "full_version_string_1234", + }, + 2: { + Version: "full_version_string_1235", + }, + 3: { + Version: "full_version_string_1236", + }, + }, + steps: []StepData{ + { + ydbopsInvocation: []string{ + "--endpoint", "grpcs://localhost:2135", + "--verbose", + "--availability-mode", "strong", + "--user", mock.TestUser, + "--cms-query-interval", "1", + "run", + "--storage", + "--version", "!=full_version_string_1234", + "--payload", filepath.Join(".", "mock", "noop-payload.sh"), + "--ca-file", filepath.Join(".", "test-data", "ssl-data", "ca.crt"), + }, + expectedRequests: []proto.Message{ + &Ydb_Auth.LoginRequest{ + User: mock.TestUser, + Password: mock.TestPassword, + }, + &Ydb_Maintenance.ListClusterNodesRequest{}, + &Ydb_Cms.ListDatabasesRequest{}, + &Ydb_Discovery.WhoAmIRequest{}, + &Ydb_Maintenance.ListMaintenanceTasksRequest{ + User: &mock.TestUser, + }, + &Ydb_Maintenance.CreateMaintenanceTaskRequest{ + TaskOptions: &Ydb_Maintenance.MaintenanceTaskOptions{ + TaskUid: "task-UUID-1", + Description: "Rolling restart maintenance task", + AvailabilityMode: Ydb_Maintenance.AvailabilityMode_AVAILABILITY_MODE_STRONG, + }, + ActionGroups: mock.MakeActionGroupsFromNodeIds(2, 3), + }, + &Ydb_Maintenance.CompleteActionRequest{ + ActionUids: []*Ydb_Maintenance.ActionUid{ + { + TaskUid: "task-UUID-1", + GroupId: "group-UUID-1", + ActionId: "action-UUID-1", + }, + }, + }, + &Ydb_Maintenance.RefreshMaintenanceTaskRequest{ + TaskUid: "task-UUID-1", + }, + &Ydb_Maintenance.CompleteActionRequest{ + ActionUids: []*Ydb_Maintenance.ActionUid{ + { + TaskUid: "task-UUID-1", + GroupId: "group-UUID-2", + ActionId: "action-UUID-2", + }, + }, + }, + }, + }, + }, + }, + ), Entry("happy path, restart dynnodes by tenant name", TestCase{ nodeConfiguration: [][]uint32{ {1, 2, 3, 4, 5, 6, 7, 8},