diff --git a/airflow/utils/json.py b/airflow/utils/json.py index f59d82009049b..6d1cc5f036d50 100644 --- a/airflow/utils/json.py +++ b/airflow/utils/json.py @@ -54,6 +54,6 @@ def _default(obj): return float(obj) elif k8s is not None and isinstance(obj, k8s.V1Pod): from airflow.kubernetes.pod_generator import PodGenerator - PodGenerator.serialize_pod(obj) + return PodGenerator.serialize_pod(obj) raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable") diff --git a/airflow/www/views.py b/airflow/www/views.py index 5bf201be47988..341c759912322 100644 --- a/airflow/www/views.py +++ b/airflow/www/views.py @@ -47,6 +47,7 @@ from sqlalchemy import and_, desc, func, or_, union_all from sqlalchemy.orm import joinedload from wtforms import SelectField, validators +from airflow.utils import json as utils_json import airflow from airflow import models, plugins_manager, settings @@ -2444,7 +2445,7 @@ def task_instances(self): ti.task_id: alchemy_to_dict(ti) for ti in dag.get_task_instances(dttm, dttm)} - return json.dumps(task_instances) + return json.dumps(task_instances, cls=utils_json.AirflowJsonEncoder) class ConfigurationView(AirflowBaseView): diff --git a/tests/utils/test_json.py b/tests/utils/test_json.py index d48f7090413dc..69363b84feb8d 100644 --- a/tests/utils/test_json.py +++ b/tests/utils/test_json.py @@ -58,6 +58,29 @@ def test_encode_numpy_float(self): '3.76953125' ) + def test_encode_k8s_v1pod(self): + from kubernetes.client import models as k8s + + pod = k8s.V1Pod( + metadata=k8s.V1ObjectMeta( + name="foo", + namespace="bar", + ), + spec=k8s.V1PodSpec( + containers=[ + k8s.V1Container( + name="foo", + image="bar", + ) + ] + ) + ) + self.assertEqual( + json.loads(json.dumps(pod, cls=utils_json.AirflowJsonEncoder)), + {"metadata": {"name": "foo", "namespace": "bar"}, + "spec": {"containers": [{"image": "bar", "name": "foo"}]}} + ) + def test_encode_raises(self): self.assertRaisesRegex(TypeError, "^.*is not JSON serializable$",