// Input file as a required positional argument
if flags.NArg() == 0 {
return fmt.Errorf("please provide a path to an input file")
+ } else if flags.NArg() > 1 {
+ return fmt.Errorf("please provide just one input file argument")
}
- srcPath := &os.Args[flags.NFlag()+1]
+ srcPath := &os.Args[len(os.Args)-1]
// Validations
if *srcPath == "" {
allUsers := make(map[string]arvados.User)
userIDToUUID := make(map[string]string) // Index by email or username
dupedEmails := make(map[string][]arvados.User)
+ emptyUserIDs := []string{}
processedUsers := make(map[string]bool)
results, err := GetAll(cfg.Client, "users", arvados.ResourceListParams{}, &UserList{})
if err != nil {
return err
}
if uID == "" {
- return fmt.Errorf("%s is empty for user with uuid %q", cfg.UserID, u.UUID)
+ if u.UUID != cfg.AnonUserUUID && u.UUID != cfg.SysUserUUID {
+ emptyUserIDs = append(emptyUserIDs, u.UUID)
+ log.Printf("Empty %s found in user %s - ignoring", cfg.UserID, u.UUID)
+ }
+ continue
}
if cfg.CaseInsensitive {
uID = strings.ToLower(uID)
log.Printf("User update successes: %d, skips: %d, failures: %d", len(updatesSucceeded), len(updatesSkipped), len(updatesFailed))
- // Report duplicated emails detection
+ var errors []string
if len(dupedEmails) > 0 {
emails := make([]string, len(dupedEmails))
i := 0
emails[i] = e
i++
}
- return fmt.Errorf("skipped %d duplicated email address(es) in the cluster's local user list: %v", len(dupedEmails), emails)
+ errors = append(errors, fmt.Sprintf("skipped %d duplicated email address(es) in the cluster's local user list: %v", len(dupedEmails), emails))
+ }
+ if len(emptyUserIDs) > 0 {
+ errors = append(errors, fmt.Sprintf("skipped %d user account(s) with empty %s: %v", len(emptyUserIDs), cfg.UserID, emptyUserIDs))
+ }
+ if len(errors) > 0 {
+ return fmt.Errorf("%s", strings.Join(errors, "\n"))
}
return nil
func needsUpdating(user arvados.User, record userRecord) bool {
userData := userRecord{"", user.FirstName, user.LastName, user.IsActive, user.IsAdmin}
- recordData := userRecord{"", record.FirstName, record.LastName, record.Active, record.Admin}
+ recordData := userRecord{"", record.FirstName, record.LastName, record.Active, record.Active && record.Admin}
return userData != recordData
}
}
}
}
- allUsers[record.UserID] = user
if createRequired {
log.Printf("Created user %q", record.UserID)
}
}
func (s *TestSuite) TestParseFlagsWrongUserID(c *C) {
- os.Args = []string{"cmd", "-user-id=nickname", "/tmp/somefile.csv"}
+ os.Args = []string{"cmd", "-user-id", "nickname", "/tmp/somefile.csv"}
err := ParseFlags(&ConfigParams{})
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, ".*user ID must be one of:.*")
func (s *TestSuite) TestParseFlagsWithOptionalFlags(c *C) {
cfg := ConfigParams{}
- os.Args = []string{"cmd", "-verbose", "-deactivate-unlisted", "-user-id=username", "/tmp/somefile.csv"}
+ os.Args = []string{"cmd", "-verbose", "-deactivate-unlisted", "-user-id", "username", "/tmp/somefile.csv"}
err := ParseFlags(&cfg)
c.Assert(err, IsNil)
c.Assert(cfg.Path, Equals, "/tmp/somefile.csv")
func (s *TestSuite) TestDeactivateUnlisted(c *C) {
localUserUuidRegex := regexp.MustCompile(fmt.Sprintf("^%s-tpzed-[0-9a-z]{15}$", s.cfg.ClusterID))
+
+ var user1 arvados.User
+ for _, nr := range []int{1, 2} {
+ var newUser arvados.User
+ err := CreateUser(s.cfg.Client, &newUser, map[string]string{
+ "email": fmt.Sprintf("user%d@example.com", nr),
+ "first_name": "Example",
+ "last_name": fmt.Sprintf("User%d", nr),
+ "is_active": "true",
+ "is_admin": "false",
+ })
+ c.Assert(err, IsNil)
+ c.Assert(newUser.IsActive, Equals, true)
+ if nr == 1 {
+ user1 = newUser // for later confirmation
+ }
+ }
+
users, err := ListUsers(s.cfg.Client)
c.Assert(err, IsNil)
previouslyActiveUsers := 0
previouslyActiveUsers++
}
}
- // At least 3 active users: System root, Anonymous and the current user.
- // Other active users should exist from fixture.
+ // Active users: System root, Anonymous, current user and the
+ // ones just created (other active users may exist from fixture).
c.Logf("Initial active users count: %d", previouslyActiveUsers)
- c.Assert(previouslyActiveUsers > 3, Equals, true)
+ c.Assert(previouslyActiveUsers > 5, Equals, true)
- s.cfg.DeactivateUnlisted = true
- s.cfg.Verbose = true
+ // Here we omit user2@example.com from the CSV file.
data := [][]string{
- {"user1@example.com", "Example", "User1", "0", "0"},
+ {"user1@example.com", "Example", "User1", "1", "0"},
}
tmpfile, err := MakeTempCSVFile(data)
c.Assert(err, IsNil)
defer os.Remove(tmpfile.Name())
+
+ s.cfg.DeactivateUnlisted = true
+ s.cfg.Verbose = true
s.cfg.Path = tmpfile.Name()
err = doMain(s.cfg)
c.Assert(err, IsNil)
fmt.Sprintf("%s-tpzed-000000000000000", s.cfg.ClusterID): true,
fmt.Sprintf("%s-tpzed-anonymouspublic", s.cfg.ClusterID): true,
s.cfg.CurrentUser.UUID: true,
+ user1.UUID: true,
}
remainingActiveUUIDs := map[string]bool{}
seenUserEmails := map[string]bool{}
currentlyActiveUsers++
}
}
- // 3 active users remaining: System root, Anonymous and the current user.
+ // 4 active users remaining: System root, Anonymous, the current user
+ // and user1@example.com
c.Logf("Active local users remaining: %v", remainingActiveUUIDs)
- c.Assert(currentlyActiveUsers, Equals, 3)
+ c.Assert(currentlyActiveUsers, Equals, 4)
}
func (s *TestSuite) TestFailOnDuplicatedEmails(c *C) {
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, "skipped.*duplicated email address.*")
}
+
+func (s *TestSuite) TestFailOnEmptyUsernames(c *C) {
+ for i := range []int{1, 2} {
+ var user arvados.User
+ err := CreateUser(s.cfg.Client, &user, map[string]string{
+ "email": fmt.Sprintf("johndoe%d@example.com", i),
+ "username": "",
+ "first_name": "John",
+ "last_name": "Doe",
+ "is_active": "true",
+ "is_admin": "false",
+ })
+ c.Assert(err, IsNil)
+ c.Assert(user.Username, Equals, fmt.Sprintf("johndoe%d", i))
+ if i == 1 {
+ err = UpdateUser(s.cfg.Client, user.UUID, &user, map[string]string{
+ "username": "",
+ })
+ c.Assert(err, IsNil)
+ c.Assert(user.Username, Equals, "")
+ }
+ }
+
+ s.cfg.Verbose = true
+ data := [][]string{
+ {"johndoe0", "John", "Doe", "0", "0"},
+ }
+ tmpfile, err := MakeTempCSVFile(data)
+ c.Assert(err, IsNil)
+ defer os.Remove(tmpfile.Name())
+ s.cfg.Path = tmpfile.Name()
+ s.cfg.UserID = "username"
+ err = doMain(s.cfg)
+ c.Assert(err, NotNil)
+ c.Assert(err, ErrorMatches, "skipped 1 user account.*with empty username.*")
+}
+
+func (s *TestSuite) TestFailOnDupedUsernameAndCaseInsensitiveMatching(c *C) {
+ for _, i := range []int{1, 2} {
+ var user arvados.User
+ emailPrefix := "john"
+ if i == 1 {
+ emailPrefix = "JOHN"
+ }
+ err := CreateUser(s.cfg.Client, &user, map[string]string{
+ "email": fmt.Sprintf("%sdoe@example.com", emailPrefix),
+ "username": "",
+ "first_name": "John",
+ "last_name": "Doe",
+ "is_active": "true",
+ "is_admin": "false",
+ })
+ c.Assert(err, IsNil)
+ c.Assert(user.Username, Equals, fmt.Sprintf("%sdoe", emailPrefix))
+ }
+
+ s.cfg.Verbose = true
+ data := [][]string{
+ {"johndoe", "John", "Doe", "0", "0"},
+ }
+ tmpfile, err := MakeTempCSVFile(data)
+ c.Assert(err, IsNil)
+ defer os.Remove(tmpfile.Name())
+ s.cfg.Path = tmpfile.Name()
+ s.cfg.UserID = "username"
+ s.cfg.CaseInsensitive = true
+ err = doMain(s.cfg)
+ c.Assert(err, NotNil)
+ c.Assert(err, ErrorMatches, "case insensitive collision for username.*between.*and.*")
+}
+
+func (s *TestSuite) TestSuccessOnUsernameAndCaseSensitiveMatching(c *C) {
+ for _, i := range []int{1, 2} {
+ var user arvados.User
+ emailPrefix := "john"
+ if i == 1 {
+ emailPrefix = "JOHN"
+ }
+ err := CreateUser(s.cfg.Client, &user, map[string]string{
+ "email": fmt.Sprintf("%sdoe@example.com", emailPrefix),
+ "username": "",
+ "first_name": "John",
+ "last_name": "Doe",
+ "is_active": "true",
+ "is_admin": "false",
+ })
+ c.Assert(err, IsNil)
+ c.Assert(user.Username, Equals, fmt.Sprintf("%sdoe", emailPrefix))
+ }
+
+ s.cfg.Verbose = true
+ data := [][]string{
+ {"johndoe", "John", "Doe", "0", "0"},
+ }
+ tmpfile, err := MakeTempCSVFile(data)
+ c.Assert(err, IsNil)
+ defer os.Remove(tmpfile.Name())
+ s.cfg.Path = tmpfile.Name()
+ s.cfg.UserID = "username"
+ s.cfg.CaseInsensitive = false
+ err = doMain(s.cfg)
+ c.Assert(err, IsNil)
+}