Consistently round down timestamps

This is one of the ways we can prevent race conditions with backends that round
datetime objects or strings before persisting them.

Closes-Bug: 1622010
(cherry picked from commit 301b6a7bc7)

Conflicts:
	keystone/tests/unit/test_v3_auth.py: freezegun was added only in Newton
	keystone/tests/unit/test_v3_os_revoke.py: minor conflict

In addition to cherry-pick, time.sleep() was added to several tests.
The tests assume that some time must pass between some operations.
In Newton and later this was done in other, unrelated commits and
freezegun was used. Freezegun cannot be used in Mitaka. Because of
that, time.sleep() was added at the same places where freezegun's
tick() is used in Newton.

Change-Id: I7c6d525dfb4ec13edb360a77b27422310d545305
This commit is contained in:
Lance Bragstad 2016-09-09 22:10:02 +00:00 committed by Boris Bobrov
parent f1d9c54ef0
commit 8d12ba3736
7 changed files with 79 additions and 6 deletions

View File

@ -522,7 +522,7 @@ def isotime(at=None, subsecond=False):
# parse correctly most of the time.
if not at:
at = timeutils.utcnow()
at = timeutils.utcnow().replace(microsecond=0)
st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)

View File

@ -90,7 +90,7 @@ class RevokeEvent(object):
self.expires_at = self.expires_at.replace(microsecond=0)
if self.revoked_at is None:
self.revoked_at = timeutils.utcnow()
self.revoked_at = timeutils.utcnow().replace(microsecond=0)
if self.issued_before is None:
self.issued_before = self.revoked_at

View File

@ -11,6 +11,7 @@
# under the License.
import random
import time
import uuid
from oslo_config import cfg
@ -155,6 +156,14 @@ class AssignmentTestCase(test_v3.RestfulTestCase,
resource_url=collection_url)
self.delete(member_url)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0,
resource_url=collection_url)
@ -193,6 +202,14 @@ class AssignmentTestCase(test_v3.RestfulTestCase,
resource_url=collection_url)
self.delete(member_url)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0,
resource_url=collection_url)
@ -232,6 +249,14 @@ class AssignmentTestCase(test_v3.RestfulTestCase,
resource_url=collection_url)
self.delete(member_url)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0,
resource_url=collection_url)
@ -325,11 +350,25 @@ class AssignmentTestCase(test_v3.RestfulTestCase,
headers={'x-subject-token': token},
expected_status=http_client.OK)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
# revokes the grant from group on project.
self.assignment_api.delete_grant(role_id=self.role['id'],
project_id=self.project['id'],
group_id=self.group['id'])
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
# validates the same token again; it should not longer be valid.
self.head('/auth/tokens',
headers={'x-subject-token': token},
@ -551,6 +590,14 @@ class AssignmentTestCase(test_v3.RestfulTestCase,
self.delete(ud_entity['links']['assignment'])
self.delete(gp_entity['links']['assignment'])
self.delete(up_entity['links']['assignment'])
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
r = self.get(collection_url)
self.assertValidRoleAssignmentListResponse(
r,
@ -1414,6 +1461,14 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase,
# Delete indirect assignment
self.delete(inherited_url)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
# Check the direct assignment exists, but the inherited one does not
self.head(direct_url)
self.head(inherited_url, expected_status=http_client.NOT_FOUND)

View File

@ -17,6 +17,7 @@ import datetime
import itertools
import json
import operator
import time
import uuid
from keystoneclient.common import cms
@ -2042,6 +2043,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase):
'group_id': self.group1['id'],
'role_id': self.role1['id']})
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
user1_token = self.get_requested_token(
self.build_authentication_request(
user_id=self.user1['id'],

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import time
from oslo_config import cfg
from oslo_serialization import jsonutils
from six.moves import range
@ -213,6 +215,13 @@ class IdentityTestFilteredCase(filtering.FilterTests,
user['name'] = '%my%name%'
self.identity_api.update_user(user['id'], user)
# NOTE(breton): the sleep below is required because time
# in revocations and token was rounded down. In Newton
# release freezegun is used for this purpose instead of
# sleep. Freezegun cannot be used in Mitaka release, because
# it was not in requirements when release happened.
time.sleep(1)
url_by_name = '/users?name=%my%name%'
r = self.get(url_by_name, auth=self.auth)

View File

@ -77,7 +77,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
sample = self._blank_event()
sample['user_id'] = six.text_type(user_id)
sample['expires_at'] = six.text_type(utils.isotime(expires_at))
before_time = timeutils.utcnow()
before_time = timeutils.utcnow().replace(microsecond=0)
self.revoke_api.revoke_by_expiration(user_id, expires_at)
resp = self.get('/OS-REVOKE/events')
events = resp.json_body['events']
@ -88,7 +88,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
project_id = uuid.uuid4().hex
sample = dict()
sample['project_id'] = six.text_type(project_id)
before_time = timeutils.utcnow()
before_time = timeutils.utcnow().replace(microsecond=0)
self.revoke_api.revoke(
revoke_model.RevokeEvent(project_id=project_id))
@ -101,7 +101,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
domain_id = uuid.uuid4().hex
sample = dict()
sample['domain_id'] = six.text_type(domain_id)
before_time = timeutils.utcnow()
before_time = timeutils.utcnow().replace(microsecond=0)
self.revoke_api.revoke(
revoke_model.RevokeEvent(domain_id=domain_id))

View File

@ -96,7 +96,8 @@ def default_expire_time():
"""
expire_delta = datetime.timedelta(seconds=CONF.token.expiration)
return timeutils.utcnow() + expire_delta
expires_at = timeutils.utcnow() + expire_delta
return expires_at.replace(microsecond=0)
def audit_info(parent_audit_id):