Skip to content

Commit 6c78e90

Browse files
authored
Always use datetime.now(timezone.utc) in paasta status (#3963)
This will make it clear that we're always using UTC internally and avoids the footgun of using utcnow() I've verified that the output for warming up pods is still the same in both this version of the code as well as in status quo - as well as the same for flink and kafka workloads. There is a small change to the kafka output where now the broker times include the offset, but this is probably better than the previous output where it was unclear what offset the times were in without looking at the parenthetical humanized time
1 parent 6763397 commit 6c78e90

File tree

3 files changed

+33
-23
lines changed

3 files changed

+33
-23
lines changed

paasta_tools/cli/cmds/status.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,11 @@ def should_job_info_be_shown(cluster_state):
719719

720720

721721
def get_pod_uptime(pod_deployed_timestamp: str):
722-
pod_creation_time = datetime.strptime(pod_deployed_timestamp, "%Y-%m-%dT%H:%M:%SZ")
723-
pod_uptime = datetime.utcnow() - pod_creation_time
722+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
723+
pod_creation_time = datetime.strptime(
724+
pod_deployed_timestamp, "%Y-%m-%dT%H:%M:%SZ"
725+
).replace(tzinfo=timezone.utc)
726+
pod_uptime = datetime.now(timezone.utc) - pod_creation_time
724727
pod_uptime_total_seconds = pod_uptime.total_seconds()
725728
pod_uptime_days = divmod(pod_uptime_total_seconds, 86400)
726729
pod_uptime_hours = divmod(pod_uptime_days[1], 3600)
@@ -730,7 +733,7 @@ def get_pod_uptime(pod_deployed_timestamp: str):
730733

731734

732735
def append_pod_status(pod_status, output: List[str]):
733-
output.append(f" Pods:")
736+
output.append(" Pods:")
734737
rows: List[Union[str, Tuple[str, str, str, str]]] = [
735738
("Pod Name", "Host", "Phase", "Uptime")
736739
]
@@ -844,7 +847,7 @@ def _print_flink_status_from_job_manager(
844847
# So that paasta status -v and kubectl get pods show the same consistent result.
845848
if verbose and len(status["pod_status"]) > 0:
846849
append_pod_status(status["pod_status"], output)
847-
output.append(f" No other information available in non-running state")
850+
output.append(" No other information available in non-running state")
848851
return 0
849852

850853
if status["state"] == "running":
@@ -854,7 +857,7 @@ def _print_flink_status_from_job_manager(
854857
service=service, instance=instance, client=client
855858
)
856859
except Exception as e:
857-
output.append(PaastaColors.red(f"Exception when talking to the API:"))
860+
output.append(PaastaColors.red("Exception when talking to the API:"))
858861
output.append(str(e))
859862
return 1
860863

@@ -879,7 +882,7 @@ def _print_flink_status_from_job_manager(
879882
service=service, instance=instance, client=client
880883
)
881884
except Exception as e:
882-
output.append(PaastaColors.red(f"Exception when talking to the API:"))
885+
output.append(PaastaColors.red("Exception when talking to the API:"))
883886
output.append(str(e))
884887
return 1
885888

@@ -890,7 +893,7 @@ def _print_flink_status_from_job_manager(
890893
try:
891894
jobs = a_sync.block(get_flink_job_details, service, instance, job_ids, client)
892895
except Exception as e:
893-
output.append(PaastaColors.red(f"Exception when talking to the API:"))
896+
output.append(PaastaColors.red("Exception when talking to the API:"))
894897
output.append(str(e))
895898
return 1
896899

@@ -906,7 +909,7 @@ def _print_flink_status_from_job_manager(
906909
max(10, shutil.get_terminal_size().columns - 52), max_job_name_length
907910
)
908911

909-
output.append(f" Jobs:")
912+
output.append(" Jobs:")
910913
if verbose > 1:
911914
output.append(
912915
f' {"Job Name": <{allowed_max_job_name_length}} State Job ID Started'
@@ -1313,10 +1316,10 @@ def get_replica_state(pod: KubernetesPodV2) -> ReplicaState:
13131316
# This logic likely needs refining
13141317
main_container = get_main_container(pod)
13151318
if main_container:
1316-
# K8s API is returning timestamps in YST, so we use now() instead of utcnow()
1319+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
13171320
warming_up = (
13181321
pod.create_timestamp + main_container.healthcheck_grace_period
1319-
> datetime.now().timestamp()
1322+
> datetime.now(timezone.utc).timestamp()
13201323
)
13211324
if pod.mesh_ready is False:
13221325
if main_container.state != "running":
@@ -1418,14 +1421,17 @@ def create_replica_table(
14181421
)
14191422
if state == ReplicaState.WARMING_UP:
14201423
if verbose > 0:
1421-
warmup_duration = datetime.now().timestamp() - pod.create_timestamp
1424+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
1425+
warmup_duration = (
1426+
datetime.now(timezone.utc).timestamp() - pod.create_timestamp
1427+
)
14221428
humanized_duration = humanize.naturaldelta(
14231429
timedelta(seconds=warmup_duration)
14241430
)
14251431
grace_period_remaining = (
14261432
pod.create_timestamp
14271433
+ main_container.healthcheck_grace_period
1428-
- datetime.now().timestamp()
1434+
- datetime.now(timezone.utc).timestamp()
14291435
)
14301436
humanized_remaining = humanize.naturaldelta(
14311437
timedelta(seconds=grace_period_remaining)
@@ -1790,6 +1796,7 @@ def node_property_to_str(prop: Dict[str, Any], verbose: int) -> str:
17901796
parsed_time = datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ").replace(
17911797
tzinfo=timezone.utc
17921798
)
1799+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
17931800
now = datetime.now(timezone.utc)
17941801
return (
17951802
humanize.naturaldelta(
@@ -1825,7 +1832,7 @@ def print_kafka_status(
18251832
desired_state = annotations.get(paasta_prefixed("desired_state"))
18261833
if desired_state is None:
18271834
raise ValueError(
1828-
f"expected desired state in kafka annotation, but received none"
1835+
"expected desired state in kafka annotation, but received none"
18291836
)
18301837
output.append(f" State: {desired_state}")
18311838

@@ -1851,7 +1858,7 @@ def print_kafka_status(
18511858
)
18521859

18531860
brokers = status["brokers"]
1854-
output.append(f" Brokers:")
1861+
output.append(" Brokers:")
18551862

18561863
if verbose:
18571864
headers = ["Id", "Phase", "IP", "Pod Name", "Started"]
@@ -1864,10 +1871,11 @@ def print_kafka_status(
18641871
PaastaColors.green if broker["phase"] == "Running" else PaastaColors.red
18651872
)
18661873

1874+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
18671875
start_time = datetime.strptime(
18681876
broker["deployed_timestamp"], "%Y-%m-%dT%H:%M:%SZ"
1869-
)
1870-
delta = datetime.utcnow() - start_time
1877+
).replace(tzinfo=timezone.utc)
1878+
delta = datetime.now(timezone.utc) - start_time
18711879
formatted_start_time = f"{str(start_time)} ({humanize.naturaltime(delta)})"
18721880

18731881
if verbose:

paasta_tools/kubernetes_tools.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import os
2121
import re
2222
from datetime import datetime
23+
from datetime import timezone
2324
from enum import Enum
2425
from functools import lru_cache
2526
from inspect import currentframe
@@ -2939,7 +2940,7 @@ def recent_container_restart(
29392940
last_timestamp: Optional[int],
29402941
time_window_s: int = 900, # 15 mins
29412942
) -> bool:
2942-
min_timestamp = datetime.now().timestamp() - time_window_s
2943+
min_timestamp = datetime.now(timezone.utc).timestamp() - time_window_s
29432944
return (
29442945
restart_count > 0
29452946
and last_state == "terminated"
@@ -3661,7 +3662,8 @@ async def get_events_for_object(
36613662
)
36623663
events = events.items if events else []
36633664
if max_age_in_seconds and max_age_in_seconds > 0:
3664-
min_timestamp = datetime.now().timestamp() - max_age_in_seconds
3665+
# NOTE: the k8s API returns timestamps in UTC, so we make sure to always work in UTC
3666+
min_timestamp = datetime.now(timezone.utc).timestamp() - max_age_in_seconds
36653667
events = [
36663668
evt
36673669
for evt in events

tests/cli/test_cmds_status.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2411,16 +2411,16 @@ def test_output(
24112411
expected_output = [
24122412
f" Kafka View Url: {status['kafka_view_url']}",
24132413
f" Zookeeper: {status['zookeeper']}",
2414-
f" State: testing",
2414+
" State: testing",
24152415
f" Ready: {str(status['cluster_ready']).lower()}",
24162416
f" Health: {PaastaColors.red('unhealthy')}",
24172417
f" Reason: {status['health']['message']}",
24182418
f" Offline Partitions: {status['health']['offline_partitions']}",
24192419
f" Under Replicated Partitions: {status['health']['under_replicated_partitions']}",
2420-
f" Brokers:",
2421-
f" Id Phase Started",
2422-
f" 0 {PaastaColors.green('Running')} 2020-03-25 16:24:21 ({mock_naturaltime.return_value})",
2423-
f" 1 {PaastaColors.red('Pending')} 2020-03-25 16:24:21 ({mock_naturaltime.return_value})",
2420+
" Brokers:",
2421+
" Id Phase Started",
2422+
f" 0 {PaastaColors.green('Running')} 2020-03-25 16:24:21+00:00 ({mock_naturaltime.return_value})",
2423+
f" 1 {PaastaColors.red('Pending')} 2020-03-25 16:24:21+00:00 ({mock_naturaltime.return_value})",
24242424
]
24252425
assert expected_output == output
24262426

0 commit comments

Comments
 (0)