etcdserver/api/v2auth/auth.go
func (ou User) merge(lg *zap.Logger, nu User, s PasswordStore) (User, error) {
	var out User
	if ou.User != nu.User {
		return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", ou.User, nu.User)
	}
	out.User = ou.User
	if nu.Password != "" {
		hash, err := s.HashPassword(nu.Password)
		if err != nil {
			return ou, err
		}
		out.Password = hash
	} else {
		out.Password = ou.Password
	}
	currentRoles := types.NewUnsafeSet(ou.Roles...)
	for _, g := range nu.Grant {
		if currentRoles.Contains(g) {
			if lg != nil {
				lg.Warn(
					"attempted to grant a duplicate role for a user",
					zap.String("user-name", nu.User),
					zap.String("role-name", g),
				)
			} else {
				plog.Noticef("granting duplicate role %s for user %s", g, nu.User)
			}
			return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, nu.User))
		}
		currentRoles.Add(g)
	}
	for _, r := range nu.Revoke {
		if !currentRoles.Contains(r) {
			if lg != nil {
				lg.Warn(
					"attempted to revoke a ungranted role for a user",
					zap.String("user-name", nu.User),
					zap.String("role-name", r),
				)
			} else {
				plog.Noticef("revoking ungranted role %s for user %s", r, nu.User)
			}
			return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, nu.User))
		}
		currentRoles.Remove(r)
	}
	out.Roles = currentRoles.Values()
	sort.Strings(out.Roles)
	return out, nil
}
func (ou User) merge(lg *zap.Logger, nu User, s PasswordStore) (User, error) {
	var out User
	if ou.User != nu.User {
		return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", ou.User, nu.User)
	}
	out.User = ou.User
	if nu.Password != "" {
		hash, err := s.HashPassword(nu.Password)
		if err != nil {
			return ou, err
		}
		out.Password = hash
	} else {
		out.Password = ou.Password
	}
	currentRoles := types.NewUnsafeSet(ou.Roles...)
	for _, g := range nu.Grant {
		if currentRoles.Contains(g) {
			if lg != nil {
				lg.Warn(
					"attempted to grant a duplicate role for a user",
					zap.String("user-name", nu.User),
					zap.String("role-name", g),
				)
			} else {
				plog.Noticef("granting duplicate role %s for user %s", g, nu.User)
			}
			return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, nu.User))
		}
		currentRoles.Add(g)
	}
	for _, r := range nu.Revoke {
		if !currentRoles.Contains(r) {
			if lg != nil {
				lg.Warn(
					"attempted to revoke a ungranted role for a user",
					zap.String("user-name", nu.User),
					zap.String("role-name", r),
				)
			} else {
				plog.Noticef("revoking ungranted role %s for user %s", r, nu.User)
			}
			return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, nu.User))
		}
		currentRoles.Remove(r)
	}
	out.Roles = currentRoles.Values()
	sort.Strings(out.Roles)
	return out, nil
}
// merge applies the properties of the passed-in User to the User on which it
// is called and returns a new User with these modifications applied. Think of
// all Users as immutable sets of data. Merge allows you to perform the set
// operations (desired grants and revokes) atomically