diff --git a/cmd/cli/main.go b/cmd/cli/main.go index f03b52a2f..2e1f91ec5 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -25,7 +25,15 @@ var ( GoVersion = "unknown" ) -var moiraValidVersions = []string{"2.3", "2.6", "2.7", "2.9", "2.11", "2.12", "2.13"} +var moiraValidVersions = []string{ + "2.3", + "2.6", + "2.7", + "2.9", + "2.11", + "2.12", + "2.13", +} var ( configFileName = flag.String("config", "/etc/moira/cli.yml", "Path to configuration file") diff --git a/cmd/cli/teams_names.go b/cmd/cli/teams_names.go index 893d60553..59560495b 100644 --- a/cmd/cli/teams_names.go +++ b/cmd/cli/teams_names.go @@ -104,12 +104,14 @@ func groupTeamsByNames(logger moira.Logger, teamMaps []map[string]string) (map[s return nil, err } - teamWithNameList, exists := teamsByNameMap[team.Name] + lowercaseTeamName := strings.ToLower(team.Name) + + teamWithNameList, exists := teamsByNameMap[lowercaseTeamName] if exists { teamWithNameList = append(teamWithNameList, team) - teamsByNameMap[team.Name] = teamWithNameList + teamsByNameMap[lowercaseTeamName] = teamWithNameList } else { - teamsByNameMap[team.Name] = []teamWithID{team} + teamsByNameMap[lowercaseTeamName] = []teamWithID{team} } } } @@ -122,8 +124,10 @@ func groupTeamsByNames(logger moira.Logger, teamMaps []map[string]string) (map[s } func updateTeamsInPipe(ctx context.Context, logger moira.Logger, pipe goredis.Pipeliner, teamsByNameMap map[string][]teamWithID) error { - for teamName, teams := range teamsByNameMap { + for _, teams := range teamsByNameMap { for i, team := range teams { + prevName := team.Name + if i > 0 { // there more than 1 team with same name, so updating teams by adding digit to the name end team.Name += strconv.FormatInt(int64(i), 10) @@ -138,7 +142,7 @@ func updateTeamsInPipe(ctx context.Context, logger moira.Logger, pipe goredis.Pi logger.Error(). Error(err). String("team_id", team.ID). - String("prev_team_name", teamName). + String("prev_team_name", prevName). String("new_team_name", team.Name). Msg("failed to update team name") @@ -151,7 +155,7 @@ func updateTeamsInPipe(ctx context.Context, logger moira.Logger, pipe goredis.Pi logger.Error(). Error(err). String("team_id", team.ID). - String("prev_team_name", teamName). + String("prev_team_name", prevName). String("new_team_name", team.Name). Msg("failed to add team name to redis hash") diff --git a/cmd/cli/teams_names_test.go b/cmd/cli/teams_names_test.go new file mode 100644 index 000000000..0d9f93514 --- /dev/null +++ b/cmd/cli/teams_names_test.go @@ -0,0 +1,197 @@ +package main + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/moira-alert/moira/database/redis" + logging "github.com/moira-alert/moira/logging/zerolog_adapter" + + . "github.com/smartystreets/goconvey/convey" +) + +var testTeams = []teamWithID{ + { + teamStorageElement: teamStorageElement{ + Name: "First team", + Description: "first desc", + }, + ID: "team1", + }, + { + teamStorageElement: teamStorageElement{ + Name: "Second team", + Description: "second desc", + }, + ID: "team2", + }, + { + teamStorageElement: teamStorageElement{ + Name: "Third team", + Description: "third desc", + }, + ID: "team3", + }, + { + teamStorageElement: teamStorageElement{ + Name: "Fourth team", + Description: "fourth desc", + }, + ID: "team4", + }, +} + +func Test_fillTeamNamesHash(t *testing.T) { + Convey("Test filling \"moira-teams-by-names\" redis hash", t, func() { + conf := getDefault() + logger, err := logging.ConfigureLog(conf.LogFile, conf.LogLevel, "test", conf.LogPrettyFormat) + if err != nil { + t.Fatal(err) + } + + db := redis.NewTestDatabase(logger) + db.Flush() + defer db.Flush() + + ctx := context.Background() + client := db.Client() + + Convey("with empty database", func() { + err = fillTeamNamesHash(logger, db) + So(err, ShouldBeNil) + + res, existErr := client.Exists(ctx, teamsByNamesKey).Result() + So(existErr, ShouldBeNil) + So(res, ShouldEqual, 0) + }) + + Convey("with teams which have unique names", func() { + defer db.Flush() + + var teamNames, actualTeamNames map[string]string + + teamNames = make(map[string]string, len(testTeams)) + + for _, team := range testTeams { + var teamBytes []byte + + teamBytes, err = getTeamBytes(team) + So(err, ShouldBeNil) + + err = client.HSet(ctx, teamsKey, team.ID, teamBytes).Err() + So(err, ShouldBeNil) + + teamNames[strings.ToLower(team.Name)] = team.ID + } + + err = fillTeamNamesHash(logger, db) + So(err, ShouldBeNil) + + actualTeamNames, err = client.HGetAll(ctx, teamsByNamesKey).Result() + So(err, ShouldBeNil) + So(actualTeamNames, ShouldResemble, teamNames) + }) + + Convey("with teams no unique names", func() { + defer db.Flush() + + testTeams[0].Name = "Team name" + testTeams[1].Name = "teaM name" + testTeams[2].Name = "Team name" + + for _, team := range testTeams { + var teamBytes []byte + + teamBytes, err = getTeamBytes(team) + So(err, ShouldBeNil) + + err = client.HSet(ctx, teamsKey, team.ID, teamBytes).Err() + So(err, ShouldBeNil) + } + + err = fillTeamNamesHash(logger, db) + So(err, ShouldBeNil) + + var actualTeamNames map[string]string + + actualTeamNames, err = client.HGetAll(ctx, teamsByNamesKey).Result() + So(err, ShouldBeNil) + So(actualTeamNames, ShouldHaveLength, len(testTeams)) + + expectedLowercasedTeamNames := []string{"team name", "team name1", "team name2", strings.ToLower(testTeams[3].Name)} + for _, name := range expectedLowercasedTeamNames { + _, ok := actualTeamNames[name] + So(ok, ShouldBeTrue) + } + + for i, team := range testTeams { + Convey(fmt.Sprintf("for team %v fields ok", i), func() { + res, err := client.HGet(ctx, teamsKey, team.ID).Result() + So(err, ShouldBeNil) + + actualTeam, err := unmarshalTeam(team.ID, []byte(res)) + So(err, ShouldBeNil) + So(actualTeam.ID, ShouldEqual, team.ID) + So(actualTeam.Description, ShouldEqual, team.Description) + if i < 3 { + So(actualTeam.Name, ShouldBeIn, []string{team.Name, team.Name + "1", team.Name + "2"}) + } else { + So(actualTeam.Name, ShouldEqual, team.Name) + } + }) + } + }) + }) +} + +func Test_removeTeamNamesHash(t *testing.T) { + Convey("Test removing \"moira-teams-by-names\" hash", t, func() { + conf := getDefault() + logger, err := logging.ConfigureLog(conf.LogFile, conf.LogLevel, "test", conf.LogPrettyFormat) + if err != nil { + t.Fatal(err) + } + + db := redis.NewTestDatabase(logger) + db.Flush() + defer db.Flush() + + ctx := context.Background() + client := db.Client() + + Convey("with empty database", func() { + err = removeTeamNamesHash(logger, db) + So(err, ShouldBeNil) + + res, existErr := client.Exists(ctx, teamsByNamesKey).Result() + So(existErr, ShouldBeNil) + So(res, ShouldEqual, 0) + }) + + Convey("with filled teams and teams by names hashes", func() { + defer db.Flush() + + for _, team := range testTeams { + var teamBytes []byte + + teamBytes, err = getTeamBytes(team) + So(err, ShouldBeNil) + + err = client.HSet(ctx, teamsKey, team.ID, teamBytes).Err() + So(err, ShouldBeNil) + + err = client.HSet(ctx, teamsByNamesKey, strings.ToLower(team.Name), team.ID).Err() + So(err, ShouldBeNil) + } + + err = removeTeamNamesHash(logger, db) + So(err, ShouldBeNil) + + res, existErr := client.Exists(ctx, teamsByNamesKey).Result() + So(existErr, ShouldBeNil) + So(res, ShouldEqual, 0) + }) + }) +}