indicate that REST API timestamps are UTC

use iso8601 Z suffix for UTC timestamps

use dateutil to parse dates from proxy, as well

even though CHP uses iso8601 UTC timestamps, we no longer assume CHP, so use more general parsing

in our db we are stuck with naïve datetime objects, so use those internally.
But ensure we put 'Z' on timestamps we ship externally
This commit is contained in:
Min RK
2018-03-21 14:18:02 +01:00
parent 05b2bf4c96
commit fd40e27be4
7 changed files with 25 additions and 15 deletions

View File

@@ -8,7 +8,7 @@ import json
from urllib.parse import quote
from oauth2.web.tornado import OAuth2Handler
from tornado import web, gen
from tornado import web
from .. import orm
from ..user import User

View File

@@ -10,7 +10,7 @@ from tornado import web
from .. import orm
from ..handlers import BaseHandler
from ..utils import url_path_join
from ..utils import isoformat, url_path_join
class APIHandler(BaseHandler):
@@ -103,7 +103,7 @@ class APIHandler(BaseHandler):
last_activity = user.last_activity
# don't call isoformat if last_activity is None
if last_activity:
last_activity = last_activity.isoformat()
last_activity = isoformat(last_activity)
model = {
'kind': 'user',
@@ -122,7 +122,7 @@ class APIHandler(BaseHandler):
for name, spawner in user.spawners.items():
last_activity = spawner.orm_spawner.last_activity
if last_activity:
last_activity = last_activity.isoformat()
last_activity = isoformat(last_activity)
if spawner.ready:
servers[name] = s = {
'name': name,

View File

@@ -8,7 +8,7 @@ import asyncio
import atexit
import binascii
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from datetime import datetime, timezone
from getpass import getuser
import logging
from operator import itemgetter
@@ -22,8 +22,8 @@ from urllib.parse import urlparse
if sys.version_info[:2] < (3, 3):
raise ValueError("Python < 3.3 not supported: %s" % sys.version)
from dateutil.parser import parse as parse_date
from jinja2 import Environment, FileSystemLoader, PrefixLoader, ChoiceLoader
from sqlalchemy.exc import OperationalError
from tornado.httpclient import AsyncHTTPClient
@@ -50,7 +50,7 @@ from .services.service import Service
from . import crypto
from . import dbutil, orm
from .user import User, UserDict
from .user import UserDict
from .oauth.store import make_provider
from ._data import DATA_FILES_PATH
from .log import CoroutineLogFormatter, log_request
@@ -59,7 +59,6 @@ from .traitlets import URLPrefix, Command
from .utils import (
maybe_future,
url_path_join,
ISO8601_ms, ISO8601_s,
print_stacks, print_ps_info,
)
# classes for config
@@ -1581,10 +1580,11 @@ class JupyterHub(Application):
if spawner is None:
self.log.warning("Found no spawner for route: %s", route)
continue
try:
dt = datetime.strptime(route_data['last_activity'], ISO8601_ms)
except Exception:
dt = datetime.strptime(route_data['last_activity'], ISO8601_s)
dt = parse_date(route_data['last_activity'])
if dt.tzinfo:
# strip timezone info to naïve UTC datetime
dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
if user.last_activity:
user.last_activity = max(user.last_activity, dt)
else:

View File

@@ -26,7 +26,7 @@ from ..spawner import LocalProcessSpawner
from ..utils import maybe_future, url_path_join
from ..metrics import (
SERVER_SPAWN_DURATION_SECONDS, ServerSpawnStatus,
PROXY_ADD_DURATION_SECONDS, ProxyAddStatus
PROXY_ADD_DURATION_SECONDS, ProxyAddStatus,
)
# pattern for the authentication token header

View File

@@ -10,7 +10,6 @@ from oauth2.error import ClientNotFoundError
from sqlalchemy import inspect
from tornado import gen
from tornado.log import app_log
from traitlets import HasTraits, Any, Dict, default
from .utils import maybe_future, url_path_join

View File

@@ -6,6 +6,7 @@
import asyncio
from binascii import b2a_hex
import concurrent.futures
from datetime import datetime, timezone
import random
import errno
import hashlib
@@ -15,7 +16,6 @@ import os
import socket
import sys
import threading
from threading import Thread
import uuid
import warnings
@@ -39,6 +39,16 @@ ISO8601_ms = '%Y-%m-%dT%H:%M:%S.%fZ'
ISO8601_s = '%Y-%m-%dT%H:%M:%SZ'
def isoformat(dt):
"""Render a datetime object as an ISO 8601 UTC timestamp
Naïve datetime objects are assumed to be UTC
"""
if dt.tzinfo:
dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
return dt.isoformat() + 'Z'
def can_connect(ip, port):
"""Check if we can connect to an ip:port.

View File

@@ -4,6 +4,7 @@ tornado>=4.1
jinja2
pamela
python-oauth2>=1.0
python-dateutil
SQLAlchemy>=1.1
requests
prometheus_client>=0.0.21