apache-airflow-providers-edge3 1.0.0rc1__py3-none-any.whl → 1.1.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airflow/providers/edge3/__init__.py +1 -1
- airflow/providers/edge3/cli/api_client.py +2 -3
- airflow/providers/edge3/cli/edge_command.py +235 -10
- airflow/providers/edge3/example_dags/integration_test.py +23 -9
- airflow/providers/edge3/executors/edge_executor.py +16 -2
- airflow/providers/edge3/get_provider_info.py +8 -1
- airflow/providers/edge3/models/edge_worker.py +53 -2
- airflow/providers/edge3/version_compat.py +0 -1
- airflow/providers/edge3/worker_api/auth.py +2 -3
- airflow/providers/edge3/worker_api/routes/worker.py +15 -2
- {apache_airflow_providers_edge3-1.0.0rc1.dist-info → apache_airflow_providers_edge3-1.1.0rc1.dist-info}/METADATA +29 -17
- {apache_airflow_providers_edge3-1.0.0rc1.dist-info → apache_airflow_providers_edge3-1.1.0rc1.dist-info}/RECORD +14 -14
- {apache_airflow_providers_edge3-1.0.0rc1.dist-info → apache_airflow_providers_edge3-1.1.0rc1.dist-info}/WHEEL +0 -0
- {apache_airflow_providers_edge3-1.0.0rc1.dist-info → apache_airflow_providers_edge3-1.1.0rc1.dist-info}/entry_points.txt +0 -0
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
|
|
29
29
|
|
30
30
|
__all__ = ["__version__"]
|
31
31
|
|
32
|
-
__version__ = "1.
|
32
|
+
__version__ = "1.1.0"
|
33
33
|
|
34
34
|
if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
|
35
35
|
"2.10.0"
|
@@ -82,10 +82,9 @@ def _make_generic_request(method: str, rest_path: str, data: str | None = None)
|
|
82
82
|
|
83
83
|
@cache
|
84
84
|
def jwt_generator() -> JWTGenerator:
|
85
|
-
clock_grace = conf.getint("core", "internal_api_clock_grace", fallback=30)
|
86
85
|
return JWTGenerator(
|
87
|
-
secret_key=conf.get("
|
88
|
-
valid_for=
|
86
|
+
secret_key=conf.get("api_auth", "jwt_secret"),
|
87
|
+
valid_for=conf.getint("api_auth", "jwt_leeway", fallback=30),
|
89
88
|
audience="api",
|
90
89
|
)
|
91
90
|
|
@@ -23,6 +23,7 @@ import signal
|
|
23
23
|
import sys
|
24
24
|
from dataclasses import asdict
|
25
25
|
from datetime import datetime
|
26
|
+
from getpass import getuser
|
26
27
|
from http import HTTPStatus
|
27
28
|
from multiprocessing import Process
|
28
29
|
from pathlib import Path
|
@@ -36,6 +37,8 @@ from requests import HTTPError
|
|
36
37
|
|
37
38
|
from airflow import __version__ as airflow_version, settings
|
38
39
|
from airflow.cli.cli_config import ARG_PID, ARG_VERBOSE, ActionCommand, Arg
|
40
|
+
from airflow.cli.commands.daemon_utils import run_command_with_daemon_option
|
41
|
+
from airflow.cli.simple_table import AirflowConsole
|
39
42
|
from airflow.configuration import conf
|
40
43
|
from airflow.providers.edge3 import __version__ as edge_provider_version
|
41
44
|
from airflow.providers.edge3.cli.api_client import (
|
@@ -164,7 +167,6 @@ class _EdgeWorkerCli:
|
|
164
167
|
"""Flag if job processing should be completed and no new jobs fetched for maintenance mode. """
|
165
168
|
maintenance_comments: str | None = None
|
166
169
|
"""Comments for maintenance mode."""
|
167
|
-
|
168
170
|
edge_instance: _EdgeWorkerCli | None = None
|
169
171
|
"""Singleton instance of the worker."""
|
170
172
|
|
@@ -176,6 +178,7 @@ class _EdgeWorkerCli:
|
|
176
178
|
concurrency: int,
|
177
179
|
job_poll_interval: int,
|
178
180
|
heartbeat_interval: int,
|
181
|
+
daemon: bool = False,
|
179
182
|
):
|
180
183
|
self.pid_file_path = pid_file_path
|
181
184
|
self.job_poll_interval = job_poll_interval
|
@@ -184,6 +187,7 @@ class _EdgeWorkerCli:
|
|
184
187
|
self.queues = queues
|
185
188
|
self.concurrency = concurrency
|
186
189
|
self.free_concurrency = concurrency
|
190
|
+
self.daemon = daemon
|
187
191
|
|
188
192
|
_EdgeWorkerCli.edge_instance = self
|
189
193
|
|
@@ -271,6 +275,12 @@ class _EdgeWorkerCli:
|
|
271
275
|
setproctitle(f"airflow edge worker: {workload.ti.key}")
|
272
276
|
|
273
277
|
try:
|
278
|
+
base_url = conf.get("api", "base_url", fallback="/")
|
279
|
+
# If it's a relative URL, use localhost:8080 as the default
|
280
|
+
if base_url.startswith("/"):
|
281
|
+
base_url = f"http://localhost:8080{base_url}"
|
282
|
+
default_execution_api_server = f"{base_url.rstrip('/')}/execution/"
|
283
|
+
|
274
284
|
supervise(
|
275
285
|
# This is the "wrong" ti type, but it duck types the same. TODO: Create a protocol for this.
|
276
286
|
# Same like in airflow/executors/local_executor.py:_execute_work()
|
@@ -278,7 +288,9 @@ class _EdgeWorkerCli:
|
|
278
288
|
dag_rel_path=workload.dag_rel_path,
|
279
289
|
bundle_info=workload.bundle_info,
|
280
290
|
token=workload.token,
|
281
|
-
server=conf.get(
|
291
|
+
server=conf.get(
|
292
|
+
"core", "execution_api_server_url", fallback=default_execution_api_server
|
293
|
+
),
|
282
294
|
log_path=workload.log_path,
|
283
295
|
)
|
284
296
|
return 0
|
@@ -332,7 +344,8 @@ class _EdgeWorkerCli:
|
|
332
344
|
if e.response.status_code == HTTPStatus.NOT_FOUND:
|
333
345
|
raise SystemExit("Error: API endpoint is not ready, please set [edge] api_enabled=True.")
|
334
346
|
raise SystemExit(str(e))
|
335
|
-
|
347
|
+
if not self.daemon:
|
348
|
+
_write_pid_to_pidfile(self.pid_file_path)
|
336
349
|
signal.signal(signal.SIGINT, _EdgeWorkerCli.signal_handler)
|
337
350
|
signal.signal(SIG_STATUS, _EdgeWorkerCli.signal_handler)
|
338
351
|
signal.signal(signal.SIGTERM, self.shutdown_handler)
|
@@ -358,7 +371,8 @@ class _EdgeWorkerCli:
|
|
358
371
|
except EdgeWorkerVersionException:
|
359
372
|
logger.info("Version mismatch of Edge worker and Core. Quitting worker anyway.")
|
360
373
|
finally:
|
361
|
-
|
374
|
+
if not self.daemon:
|
375
|
+
remove_existing_pidfile(self.pid_file_path)
|
362
376
|
|
363
377
|
def loop(self):
|
364
378
|
"""Run a loop of scheduling and monitoring tasks."""
|
@@ -463,6 +477,9 @@ class _EdgeWorkerCli:
|
|
463
477
|
_EdgeWorkerCli.maintenance_comments = worker_info.maintenance_comments
|
464
478
|
else:
|
465
479
|
_EdgeWorkerCli.maintenance_comments = None
|
480
|
+
if worker_info.state == EdgeWorkerState.SHUTDOWN_REQUEST:
|
481
|
+
logger.info("Shutdown requested!")
|
482
|
+
_EdgeWorkerCli.drain = True
|
466
483
|
|
467
484
|
worker_state_changed = worker_info.state != state
|
468
485
|
except EdgeWorkerVersionException:
|
@@ -479,10 +496,8 @@ class _EdgeWorkerCli:
|
|
479
496
|
return
|
480
497
|
|
481
498
|
|
482
|
-
@cli_utils.action_cli(check_db=False)
|
483
499
|
@providers_configuration_loaded
|
484
|
-
def
|
485
|
-
"""Start Airflow Edge Worker."""
|
500
|
+
def _launch_worker(args):
|
486
501
|
print(settings.HEADER)
|
487
502
|
print(EDGE_WORKER_HEADER)
|
488
503
|
|
@@ -493,14 +508,31 @@ def worker(args):
|
|
493
508
|
concurrency=args.concurrency,
|
494
509
|
job_poll_interval=conf.getint("edge", "job_poll_interval"),
|
495
510
|
heartbeat_interval=conf.getint("edge", "heartbeat_interval"),
|
511
|
+
daemon=args.daemon,
|
496
512
|
)
|
497
513
|
edge_worker.start()
|
498
514
|
|
499
515
|
|
516
|
+
@cli_utils.action_cli(check_db=False)
|
517
|
+
@providers_configuration_loaded
|
518
|
+
def worker(args):
|
519
|
+
"""Start Airflow Edge Worker."""
|
520
|
+
umask = args.umask or conf.get("edge", "worker_umask", fallback=settings.DAEMON_UMASK)
|
521
|
+
|
522
|
+
run_command_with_daemon_option(
|
523
|
+
args=args,
|
524
|
+
process_name=EDGE_WORKER_PROCESS_NAME,
|
525
|
+
callback=lambda: _launch_worker(args),
|
526
|
+
should_setup_logging=True,
|
527
|
+
pid_file=_pid_file_path(args.pid),
|
528
|
+
umask=umask,
|
529
|
+
)
|
530
|
+
|
531
|
+
|
500
532
|
@cli_utils.action_cli(check_db=False)
|
501
533
|
@providers_configuration_loaded
|
502
534
|
def status(args):
|
503
|
-
"""Check for Airflow Edge Worker status."""
|
535
|
+
"""Check for Airflow Local Edge Worker status."""
|
504
536
|
pid = _get_pid(args.pid)
|
505
537
|
|
506
538
|
# Send Signal as notification to drop status JSON
|
@@ -526,7 +558,7 @@ def status(args):
|
|
526
558
|
@cli_utils.action_cli(check_db=False)
|
527
559
|
@providers_configuration_loaded
|
528
560
|
def maintenance(args):
|
529
|
-
"""Set or Unset maintenance mode of worker."""
|
561
|
+
"""Set or Unset maintenance mode of local edge worker."""
|
530
562
|
if args.maintenance == "on" and not args.comments:
|
531
563
|
logger.error("Comments are required when setting maintenance mode.")
|
532
564
|
sys.exit(4)
|
@@ -597,7 +629,7 @@ def maintenance(args):
|
|
597
629
|
@cli_utils.action_cli(check_db=False)
|
598
630
|
@providers_configuration_loaded
|
599
631
|
def stop(args):
|
600
|
-
"""Stop a running Airflow Edge Worker."""
|
632
|
+
"""Stop a running local Airflow Edge Worker."""
|
601
633
|
pid = _get_pid(args.pid)
|
602
634
|
# Send SIGINT
|
603
635
|
logger.info("Sending SIGINT to worker pid %i.", pid)
|
@@ -611,6 +643,109 @@ def stop(args):
|
|
611
643
|
logger.info("Worker has been shut down.")
|
612
644
|
|
613
645
|
|
646
|
+
def _check_valid_db_connection():
|
647
|
+
"""Check for a valid db connection before executing db dependent cli commands."""
|
648
|
+
db_conn = conf.get("database", "sql_alchemy_conn")
|
649
|
+
db_default = conf.get_default_value("database", "sql_alchemy_conn")
|
650
|
+
if db_conn == db_default:
|
651
|
+
raise SystemExit(
|
652
|
+
"Error: The database connection is not set. Please set the connection in the configuration file."
|
653
|
+
)
|
654
|
+
|
655
|
+
|
656
|
+
def _check_if_registered_edge_host(hostname: str):
|
657
|
+
"""Check if edge worker is registered with the db before executing dependent cli commands."""
|
658
|
+
from airflow.providers.edge3.models.edge_worker import _fetch_edge_hosts_from_db
|
659
|
+
|
660
|
+
if not _fetch_edge_hosts_from_db(hostname=hostname):
|
661
|
+
raise SystemExit(f"Error: Edge Worker {hostname} is unknown!")
|
662
|
+
|
663
|
+
|
664
|
+
@cli_utils.action_cli(check_db=False)
|
665
|
+
@providers_configuration_loaded
|
666
|
+
def list_edge_workers(args) -> None:
|
667
|
+
"""Query the db to list all registered edge workers."""
|
668
|
+
_check_valid_db_connection()
|
669
|
+
from airflow.providers.edge3.models.edge_worker import get_registered_edge_hosts
|
670
|
+
|
671
|
+
all_hosts_iter = get_registered_edge_hosts(states=args.state)
|
672
|
+
# Format and print worker info on the screen
|
673
|
+
fields = [
|
674
|
+
"worker_name",
|
675
|
+
"state",
|
676
|
+
"queues",
|
677
|
+
"maintenance_comment",
|
678
|
+
]
|
679
|
+
all_hosts = [{f: host.__getattribute__(f) for f in fields} for host in all_hosts_iter]
|
680
|
+
AirflowConsole().print_as(data=all_hosts, output=args.output)
|
681
|
+
|
682
|
+
|
683
|
+
@cli_utils.action_cli(check_db=False)
|
684
|
+
@providers_configuration_loaded
|
685
|
+
def put_remote_worker_on_maintenance(args) -> None:
|
686
|
+
"""Put remote edge worker on maintenance."""
|
687
|
+
_check_valid_db_connection()
|
688
|
+
_check_if_registered_edge_host(hostname=args.edge_hostname)
|
689
|
+
from airflow.providers.edge3.models.edge_worker import request_maintenance
|
690
|
+
|
691
|
+
request_maintenance(args.edge_hostname, args.comments)
|
692
|
+
logger.info("%s has been put on maintenance by %s.", args.edge_hostname, getuser())
|
693
|
+
|
694
|
+
|
695
|
+
@cli_utils.action_cli(check_db=False)
|
696
|
+
@providers_configuration_loaded
|
697
|
+
def remove_remote_worker_from_maintenance(args) -> None:
|
698
|
+
"""Remove remote edge worker from maintenance."""
|
699
|
+
_check_valid_db_connection()
|
700
|
+
_check_if_registered_edge_host(hostname=args.edge_hostname)
|
701
|
+
from airflow.providers.edge3.models.edge_worker import exit_maintenance
|
702
|
+
|
703
|
+
exit_maintenance(args.edge_hostname)
|
704
|
+
logger.info("%s has been removed from maintenance by %s.", args.edge_hostname, getuser())
|
705
|
+
|
706
|
+
|
707
|
+
@cli_utils.action_cli(check_db=False)
|
708
|
+
@providers_configuration_loaded
|
709
|
+
def remote_worker_update_maintenance_comment(args) -> None:
|
710
|
+
"""Update maintenance comments of the remote edge worker."""
|
711
|
+
_check_valid_db_connection()
|
712
|
+
_check_if_registered_edge_host(hostname=args.edge_hostname)
|
713
|
+
from airflow.providers.edge3.models.edge_worker import change_maintenance_comment
|
714
|
+
|
715
|
+
try:
|
716
|
+
change_maintenance_comment(args.edge_hostname, args.comments)
|
717
|
+
logger.info("Maintenance comments updated for %s by %s.", args.edge_hostname, getuser())
|
718
|
+
except TypeError:
|
719
|
+
raise SystemExit
|
720
|
+
|
721
|
+
|
722
|
+
@cli_utils.action_cli(check_db=False)
|
723
|
+
@providers_configuration_loaded
|
724
|
+
def remove_remote_worker(args) -> None:
|
725
|
+
"""Remove remote edge worker entry from db."""
|
726
|
+
_check_valid_db_connection()
|
727
|
+
_check_if_registered_edge_host(hostname=args.edge_hostname)
|
728
|
+
from airflow.providers.edge3.models.edge_worker import remove_worker
|
729
|
+
|
730
|
+
try:
|
731
|
+
remove_worker(args.edge_hostname)
|
732
|
+
logger.info("Edge Worker host %s removed by %s.", args.edge_hostname, getuser())
|
733
|
+
except TypeError:
|
734
|
+
raise SystemExit
|
735
|
+
|
736
|
+
|
737
|
+
@cli_utils.action_cli(check_db=False)
|
738
|
+
@providers_configuration_loaded
|
739
|
+
def remote_worker_request_shutdown(args) -> None:
|
740
|
+
"""Initiate the shutdown of the remote edge worker."""
|
741
|
+
_check_valid_db_connection()
|
742
|
+
_check_if_registered_edge_host(hostname=args.edge_hostname)
|
743
|
+
from airflow.providers.edge3.models.edge_worker import request_shutdown
|
744
|
+
|
745
|
+
request_shutdown(args.edge_hostname)
|
746
|
+
logger.info("Requested shutdown of Edge Worker host %s by %s.", args.edge_hostname, getuser())
|
747
|
+
|
748
|
+
|
614
749
|
ARG_CONCURRENCY = Arg(
|
615
750
|
("-c", "--concurrency"),
|
616
751
|
type=int,
|
@@ -625,11 +760,21 @@ ARG_EDGE_HOSTNAME = Arg(
|
|
625
760
|
("-H", "--edge-hostname"),
|
626
761
|
help="Set the hostname of worker if you have multiple workers on a single machine",
|
627
762
|
)
|
763
|
+
ARG_REQUIRED_EDGE_HOSTNAME = Arg(
|
764
|
+
("-H", "--edge-hostname"),
|
765
|
+
help="Set the hostname of worker if you have multiple workers on a single machine",
|
766
|
+
required=True,
|
767
|
+
)
|
628
768
|
ARG_MAINTENANCE = Arg(("maintenance",), help="Desired maintenance state", choices=("on", "off"))
|
629
769
|
ARG_MAINTENANCE_COMMENT = Arg(
|
630
770
|
("-c", "--comments"),
|
631
771
|
help="Maintenance comments to report reason. Required if maintenance is turned on.",
|
632
772
|
)
|
773
|
+
ARG_REQUIRED_MAINTENANCE_COMMENT = Arg(
|
774
|
+
("-c", "--comments"),
|
775
|
+
help="Maintenance comments to report reason. Required if enabling maintenance",
|
776
|
+
required=True,
|
777
|
+
)
|
633
778
|
ARG_WAIT_MAINT = Arg(
|
634
779
|
("-w", "--wait"),
|
635
780
|
default=False,
|
@@ -642,6 +787,36 @@ ARG_WAIT_STOP = Arg(
|
|
642
787
|
help="Wait until edge worker is shut down.",
|
643
788
|
action="store_true",
|
644
789
|
)
|
790
|
+
ARG_OUTPUT = Arg(
|
791
|
+
(
|
792
|
+
"-o",
|
793
|
+
"--output",
|
794
|
+
),
|
795
|
+
help="Output format. Allowed values: json, yaml, plain, table (default: table)",
|
796
|
+
metavar="(table, json, yaml, plain)",
|
797
|
+
choices=("table", "json", "yaml", "plain"),
|
798
|
+
default="table",
|
799
|
+
)
|
800
|
+
ARG_STATE = Arg(
|
801
|
+
(
|
802
|
+
"-s",
|
803
|
+
"--state",
|
804
|
+
),
|
805
|
+
nargs="+",
|
806
|
+
help="State of the edge worker",
|
807
|
+
)
|
808
|
+
|
809
|
+
ARG_DAEMON = Arg(
|
810
|
+
("-D", "--daemon"), help="Daemonize instead of running in the foreground", action="store_true"
|
811
|
+
)
|
812
|
+
ARG_UMASK = Arg(
|
813
|
+
("-u", "--umask"),
|
814
|
+
help="Set the umask of edge worker in daemon mode",
|
815
|
+
)
|
816
|
+
ARG_STDERR = Arg(("--stderr",), help="Redirect stderr to this file if run in daemon mode")
|
817
|
+
ARG_STDOUT = Arg(("--stdout",), help="Redirect stdout to this file if run in daemon mode")
|
818
|
+
ARG_LOG_FILE = Arg(("-l", "--log-file"), help="Location of the log file if run in daemon mode")
|
819
|
+
|
645
820
|
EDGE_COMMANDS: list[ActionCommand] = [
|
646
821
|
ActionCommand(
|
647
822
|
name=worker.__name__,
|
@@ -653,6 +828,11 @@ EDGE_COMMANDS: list[ActionCommand] = [
|
|
653
828
|
ARG_EDGE_HOSTNAME,
|
654
829
|
ARG_PID,
|
655
830
|
ARG_VERBOSE,
|
831
|
+
ARG_DAEMON,
|
832
|
+
ARG_STDOUT,
|
833
|
+
ARG_STDERR,
|
834
|
+
ARG_LOG_FILE,
|
835
|
+
ARG_UMASK,
|
656
836
|
),
|
657
837
|
),
|
658
838
|
ActionCommand(
|
@@ -686,4 +866,49 @@ EDGE_COMMANDS: list[ActionCommand] = [
|
|
686
866
|
ARG_VERBOSE,
|
687
867
|
),
|
688
868
|
),
|
869
|
+
ActionCommand(
|
870
|
+
name="list-workers",
|
871
|
+
help=list_edge_workers.__doc__,
|
872
|
+
func=list_edge_workers,
|
873
|
+
args=(
|
874
|
+
ARG_OUTPUT,
|
875
|
+
ARG_STATE,
|
876
|
+
),
|
877
|
+
),
|
878
|
+
ActionCommand(
|
879
|
+
name="remote-edge-worker-request-maintenance",
|
880
|
+
help=put_remote_worker_on_maintenance.__doc__,
|
881
|
+
func=put_remote_worker_on_maintenance,
|
882
|
+
args=(
|
883
|
+
ARG_REQUIRED_EDGE_HOSTNAME,
|
884
|
+
ARG_REQUIRED_MAINTENANCE_COMMENT,
|
885
|
+
),
|
886
|
+
),
|
887
|
+
ActionCommand(
|
888
|
+
name="remote-edge-worker-exit-maintenance",
|
889
|
+
help=remove_remote_worker_from_maintenance.__doc__,
|
890
|
+
func=remove_remote_worker_from_maintenance,
|
891
|
+
args=(ARG_REQUIRED_EDGE_HOSTNAME,),
|
892
|
+
),
|
893
|
+
ActionCommand(
|
894
|
+
name="remote-edge-worker-update-maintenance-comment",
|
895
|
+
help=remote_worker_update_maintenance_comment.__doc__,
|
896
|
+
func=remote_worker_update_maintenance_comment,
|
897
|
+
args=(
|
898
|
+
ARG_REQUIRED_EDGE_HOSTNAME,
|
899
|
+
ARG_REQUIRED_MAINTENANCE_COMMENT,
|
900
|
+
),
|
901
|
+
),
|
902
|
+
ActionCommand(
|
903
|
+
name="remove-remote-edge-worker",
|
904
|
+
help=remove_remote_worker.__doc__,
|
905
|
+
func=remove_remote_worker,
|
906
|
+
args=(ARG_REQUIRED_EDGE_HOSTNAME,),
|
907
|
+
),
|
908
|
+
ActionCommand(
|
909
|
+
name="shutdown-remote-edge-worker",
|
910
|
+
help=remote_worker_request_shutdown.__doc__,
|
911
|
+
func=remote_worker_request_shutdown,
|
912
|
+
args=(ARG_REQUIRED_EDGE_HOSTNAME,),
|
913
|
+
),
|
689
914
|
]
|
@@ -26,20 +26,24 @@ from __future__ import annotations
|
|
26
26
|
from datetime import datetime
|
27
27
|
from time import sleep
|
28
28
|
|
29
|
-
from airflow.decorators import task, task_group
|
30
29
|
from airflow.exceptions import AirflowNotFoundException
|
31
30
|
from airflow.hooks.base import BaseHook
|
32
|
-
from airflow.models.dag import DAG
|
33
|
-
from airflow.models.variable import Variable
|
34
|
-
from airflow.providers.common.compat.standard.operators import PythonOperator
|
35
|
-
from airflow.providers.standard.operators.empty import EmptyOperator
|
36
|
-
from airflow.sdk import Param
|
37
31
|
from airflow.utils.trigger_rule import TriggerRule
|
38
32
|
|
39
33
|
try:
|
40
34
|
from airflow.providers.standard.operators.bash import BashOperator
|
35
|
+
from airflow.providers.standard.operators.empty import EmptyOperator
|
36
|
+
from airflow.providers.standard.operators.python import PythonOperator
|
37
|
+
from airflow.sdk import DAG, Param, Variable, task, task_group
|
41
38
|
except ImportError:
|
39
|
+
# Airflow 2.10 compat
|
40
|
+
from airflow.decorators import task, task_group # type: ignore[no-redef,attr-defined]
|
41
|
+
from airflow.models.dag import DAG # type: ignore[no-redef,attr-defined,assignment]
|
42
|
+
from airflow.models.param import Param # type: ignore[no-redef,attr-defined]
|
43
|
+
from airflow.models.variable import Variable # type: ignore[no-redef,attr-defined]
|
42
44
|
from airflow.operators.bash import BashOperator # type: ignore[no-redef,attr-defined]
|
45
|
+
from airflow.operators.empty import EmptyOperator # type: ignore[no-redef,attr-defined]
|
46
|
+
from airflow.operators.python import PythonOperator # type: ignore[no-redef,attr-defined]
|
43
47
|
|
44
48
|
with DAG(
|
45
49
|
dag_id="integration_test",
|
@@ -53,9 +57,18 @@ with DAG(
|
|
53
57
|
"mapping_count": Param(
|
54
58
|
4,
|
55
59
|
type="integer",
|
60
|
+
minimum=1,
|
61
|
+
maximum=1024,
|
56
62
|
title="Mapping Count",
|
57
63
|
description="Amount of tasks that should be mapped",
|
58
64
|
),
|
65
|
+
"minutes_to_run": Param(
|
66
|
+
15,
|
67
|
+
type="integer",
|
68
|
+
minimum=1,
|
69
|
+
title="Minutes to run",
|
70
|
+
description="Duration in minutes the long running task should run",
|
71
|
+
),
|
59
72
|
},
|
60
73
|
) as dag:
|
61
74
|
|
@@ -146,9 +159,10 @@ with DAG(
|
|
146
159
|
[plan_to_fail(), needs_retry()] >> capture_fail()
|
147
160
|
|
148
161
|
@task
|
149
|
-
def long_running():
|
150
|
-
|
151
|
-
for
|
162
|
+
def long_running(**context):
|
163
|
+
minutes_to_run: int = context["params"]["minutes_to_run"]
|
164
|
+
print(f"This task runs for {minutes_to_run} minute{'s' if minutes_to_run > 1 else ''}.")
|
165
|
+
for i in range(minutes_to_run):
|
152
166
|
sleep(60)
|
153
167
|
print(f"Running for {i + 1} minutes now.")
|
154
168
|
print("Long running task completed.")
|
@@ -177,7 +177,11 @@ class EdgeExecutor(BaseExecutor):
|
|
177
177
|
.with_for_update(skip_locked=True)
|
178
178
|
.filter(
|
179
179
|
EdgeWorkerModel.state.not_in(
|
180
|
-
[
|
180
|
+
[
|
181
|
+
EdgeWorkerState.UNKNOWN,
|
182
|
+
EdgeWorkerState.OFFLINE,
|
183
|
+
EdgeWorkerState.OFFLINE_MAINTENANCE,
|
184
|
+
]
|
181
185
|
),
|
182
186
|
EdgeWorkerModel.last_update < (timezone.utcnow() - timedelta(seconds=heartbeat_interval * 5)),
|
183
187
|
)
|
@@ -186,7 +190,17 @@ class EdgeExecutor(BaseExecutor):
|
|
186
190
|
|
187
191
|
for worker in lifeless_workers:
|
188
192
|
changed = True
|
189
|
-
worker
|
193
|
+
# If the worker dies in maintenance mode we want to remember it, so it can start in maintenance mode
|
194
|
+
worker.state = (
|
195
|
+
EdgeWorkerState.OFFLINE_MAINTENANCE
|
196
|
+
if worker.state
|
197
|
+
in (
|
198
|
+
EdgeWorkerState.MAINTENANCE_MODE,
|
199
|
+
EdgeWorkerState.MAINTENANCE_PENDING,
|
200
|
+
EdgeWorkerState.MAINTENANCE_REQUEST,
|
201
|
+
)
|
202
|
+
else EdgeWorkerState.UNKNOWN
|
203
|
+
)
|
190
204
|
reset_metrics(worker.worker_name)
|
191
205
|
|
192
206
|
return changed
|
@@ -25,7 +25,7 @@ def get_provider_info():
|
|
25
25
|
return {
|
26
26
|
"package-name": "apache-airflow-providers-edge3",
|
27
27
|
"name": "Edge Executor",
|
28
|
-
"description": "Handle edge workers on remote sites via HTTP(s) connection and orchestrates work over distributed sites\n",
|
28
|
+
"description": "Handle edge workers on remote sites via HTTP(s) connection and orchestrates work over distributed sites.\n\nWhen tasks need to be executed on remote sites where the connection need to pass through\nfirewalls or other network restrictions, the Edge Worker can be deployed. The Edge Worker\nis a lightweight process with reduced dependencies. The worker only needs to be able to\ncommunicate with the central Airflow site via HTTPS.\n\nIn the central Airflow site the EdgeExecutor is used to orchestrate the work. The EdgeExecutor\nis a custom executor which is used to schedule tasks on the edge workers. The EdgeExecutor can co-exist\nwith other executors (for example CeleryExecutor or KubernetesExecutor) in the same Airflow site.\n\nAdditional REST API endpoints are provided to distribute tasks and manage the edge workers. The endpoints\nare provided by the API server.\n",
|
29
29
|
"plugins": [
|
30
30
|
{
|
31
31
|
"name": "edge_executor",
|
@@ -93,6 +93,13 @@ def get_provider_info():
|
|
93
93
|
"example": None,
|
94
94
|
"default": "524288",
|
95
95
|
},
|
96
|
+
"worker_umask": {
|
97
|
+
"description": "The default umask to use for edge worker when run in daemon mode\n\nThis controls the file-creation mode mask which determines the initial value of file permission bits\nfor newly created files.\n\nThis value is treated as an octal-integer.\n",
|
98
|
+
"version_added": None,
|
99
|
+
"type": "string",
|
100
|
+
"default": None,
|
101
|
+
"example": None,
|
102
|
+
},
|
96
103
|
},
|
97
104
|
}
|
98
105
|
},
|
@@ -18,6 +18,7 @@ from __future__ import annotations
|
|
18
18
|
|
19
19
|
import ast
|
20
20
|
import json
|
21
|
+
import logging
|
21
22
|
from datetime import datetime
|
22
23
|
from enum import Enum
|
23
24
|
from typing import TYPE_CHECKING
|
@@ -29,12 +30,15 @@ from airflow.models.base import Base
|
|
29
30
|
from airflow.stats import Stats
|
30
31
|
from airflow.utils import timezone
|
31
32
|
from airflow.utils.log.logging_mixin import LoggingMixin
|
33
|
+
from airflow.utils.providers_configuration_loader import providers_configuration_loaded
|
32
34
|
from airflow.utils.session import NEW_SESSION, provide_session
|
33
35
|
from airflow.utils.sqlalchemy import UtcDateTime
|
34
36
|
|
35
37
|
if TYPE_CHECKING:
|
36
38
|
from sqlalchemy.orm import Session
|
37
39
|
|
40
|
+
logger = logging.getLogger(__name__)
|
41
|
+
|
38
42
|
|
39
43
|
class EdgeWorkerVersionException(AirflowException):
|
40
44
|
"""Signal a version mismatch between core and Edge Site."""
|
@@ -51,6 +55,8 @@ class EdgeWorkerState(str, Enum):
|
|
51
55
|
"""Edge Worker is actively running a task."""
|
52
56
|
IDLE = "idle"
|
53
57
|
"""Edge Worker is active and waiting for a task."""
|
58
|
+
SHUTDOWN_REQUEST = "shutdown request"
|
59
|
+
"""Request to shutdown Edge Worker."""
|
54
60
|
TERMINATING = "terminating"
|
55
61
|
"""Edge Worker is completing work and stopping."""
|
56
62
|
OFFLINE = "offline"
|
@@ -194,6 +200,26 @@ def reset_metrics(worker_name: str) -> None:
|
|
194
200
|
)
|
195
201
|
|
196
202
|
|
203
|
+
@providers_configuration_loaded
|
204
|
+
@provide_session
|
205
|
+
def _fetch_edge_hosts_from_db(
|
206
|
+
hostname: str | None = None, states: list | None = None, session: Session = NEW_SESSION
|
207
|
+
) -> list:
|
208
|
+
query = select(EdgeWorkerModel)
|
209
|
+
if states:
|
210
|
+
query = query.where(EdgeWorkerModel.state.in_(states))
|
211
|
+
if hostname:
|
212
|
+
query = query.where(EdgeWorkerModel.worker_name == hostname)
|
213
|
+
query = query.order_by(EdgeWorkerModel.worker_name)
|
214
|
+
return session.scalars(query).all()
|
215
|
+
|
216
|
+
|
217
|
+
@providers_configuration_loaded
|
218
|
+
@provide_session
|
219
|
+
def get_registered_edge_hosts(states: list | None = None, session: Session = NEW_SESSION):
|
220
|
+
return _fetch_edge_hosts_from_db(states=states, session=session)
|
221
|
+
|
222
|
+
|
197
223
|
@provide_session
|
198
224
|
def request_maintenance(
|
199
225
|
worker_name: str, maintenance_comment: str | None, session: Session = NEW_SESSION
|
@@ -217,7 +243,14 @@ def exit_maintenance(worker_name: str, session: Session = NEW_SESSION) -> None:
|
|
217
243
|
@provide_session
|
218
244
|
def remove_worker(worker_name: str, session: Session = NEW_SESSION) -> None:
|
219
245
|
"""Remove a worker that is offline or just gone from DB."""
|
220
|
-
|
246
|
+
query = select(EdgeWorkerModel).where(EdgeWorkerModel.worker_name == worker_name)
|
247
|
+
worker: EdgeWorkerModel = session.scalar(query)
|
248
|
+
if worker.state in (EdgeWorkerState.OFFLINE, EdgeWorkerState.OFFLINE_MAINTENANCE):
|
249
|
+
session.execute(delete(EdgeWorkerModel).where(EdgeWorkerModel.worker_name == worker_name))
|
250
|
+
else:
|
251
|
+
error_message = f"Cannot remove edge worker {worker_name} as it is in {worker.state} state!"
|
252
|
+
logger.error(error_message)
|
253
|
+
raise TypeError(error_message)
|
221
254
|
|
222
255
|
|
223
256
|
@provide_session
|
@@ -227,4 +260,22 @@ def change_maintenance_comment(
|
|
227
260
|
"""Write maintenance comment in the db."""
|
228
261
|
query = select(EdgeWorkerModel).where(EdgeWorkerModel.worker_name == worker_name)
|
229
262
|
worker: EdgeWorkerModel = session.scalar(query)
|
230
|
-
worker.
|
263
|
+
if worker.state in (EdgeWorkerState.MAINTENANCE_MODE, EdgeWorkerState.OFFLINE_MAINTENANCE):
|
264
|
+
worker.maintenance_comment = maintenance_comment
|
265
|
+
else:
|
266
|
+
error_message = f"Cannot change maintenance comment as {worker_name} is not in maintenance!"
|
267
|
+
logger.error(error_message)
|
268
|
+
raise TypeError(error_message)
|
269
|
+
|
270
|
+
|
271
|
+
@provide_session
|
272
|
+
def request_shutdown(worker_name: str, session: Session = NEW_SESSION) -> None:
|
273
|
+
"""Request to shutdown the edge worker."""
|
274
|
+
query = select(EdgeWorkerModel).where(EdgeWorkerModel.worker_name == worker_name)
|
275
|
+
worker: EdgeWorkerModel = session.scalar(query)
|
276
|
+
if worker.state not in (
|
277
|
+
EdgeWorkerState.OFFLINE,
|
278
|
+
EdgeWorkerState.OFFLINE_MAINTENANCE,
|
279
|
+
EdgeWorkerState.UNKNOWN,
|
280
|
+
):
|
281
|
+
worker.state = EdgeWorkerState.SHUTDOWN_REQUEST
|
@@ -32,5 +32,4 @@ def get_base_airflow_version_tuple() -> tuple[int, int, int]:
|
|
32
32
|
return airflow_version.major, airflow_version.minor, airflow_version.micro
|
33
33
|
|
34
34
|
|
35
|
-
AIRFLOW_V_2_10_PLUS = get_base_airflow_version_tuple() >= (2, 10, 0)
|
36
35
|
AIRFLOW_V_3_0_PLUS = get_base_airflow_version_tuple() >= (3, 0, 0)
|
@@ -47,10 +47,9 @@ if AIRFLOW_V_3_0_PLUS:
|
|
47
47
|
|
48
48
|
@cache
|
49
49
|
def jwt_validator() -> JWTValidator:
|
50
|
-
clock_grace = conf.getint("core", "internal_api_clock_grace", fallback=30)
|
51
50
|
return JWTValidator(
|
52
|
-
secret_key=conf.get("
|
53
|
-
leeway=
|
51
|
+
secret_key=conf.get("api_auth", "jwt_secret"),
|
52
|
+
leeway=conf.getint("api_auth", "jwt_leeway", fallback=30),
|
54
53
|
audience="api",
|
55
54
|
)
|
56
55
|
|
@@ -114,7 +114,7 @@ _worker_queue_doc = Body(
|
|
114
114
|
|
115
115
|
|
116
116
|
def redefine_state(worker_state: EdgeWorkerState, body_state: EdgeWorkerState) -> EdgeWorkerState:
|
117
|
-
"""Redefine the state of the worker based on maintenance request."""
|
117
|
+
"""Redefine the state of the worker based on maintenance or shutdown request."""
|
118
118
|
if (
|
119
119
|
worker_state == EdgeWorkerState.MAINTENANCE_REQUEST
|
120
120
|
and body_state
|
@@ -122,7 +122,12 @@ def redefine_state(worker_state: EdgeWorkerState, body_state: EdgeWorkerState) -
|
|
122
122
|
EdgeWorkerState.MAINTENANCE_PENDING,
|
123
123
|
EdgeWorkerState.MAINTENANCE_MODE,
|
124
124
|
)
|
125
|
-
or worker_state
|
125
|
+
or worker_state
|
126
|
+
in (
|
127
|
+
EdgeWorkerState.OFFLINE_MAINTENANCE,
|
128
|
+
EdgeWorkerState.MAINTENANCE_MODE,
|
129
|
+
EdgeWorkerState.MAINTENANCE_PENDING,
|
130
|
+
)
|
126
131
|
and body_state == EdgeWorkerState.STARTING
|
127
132
|
):
|
128
133
|
return EdgeWorkerState.MAINTENANCE_REQUEST
|
@@ -133,6 +138,14 @@ def redefine_state(worker_state: EdgeWorkerState, body_state: EdgeWorkerState) -
|
|
133
138
|
if body_state == EdgeWorkerState.MAINTENANCE_MODE:
|
134
139
|
return EdgeWorkerState.IDLE
|
135
140
|
|
141
|
+
if worker_state == EdgeWorkerState.SHUTDOWN_REQUEST:
|
142
|
+
if body_state not in (
|
143
|
+
EdgeWorkerState.OFFLINE_MAINTENANCE,
|
144
|
+
EdgeWorkerState.OFFLINE,
|
145
|
+
EdgeWorkerState.UNKNOWN,
|
146
|
+
):
|
147
|
+
return EdgeWorkerState.SHUTDOWN_REQUEST
|
148
|
+
|
136
149
|
return body_state
|
137
150
|
|
138
151
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: apache-airflow-providers-edge3
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.0rc1
|
4
4
|
Summary: Provider package apache-airflow-providers-edge3 for Apache Airflow
|
5
5
|
Keywords: airflow-provider,edge3,airflow,integration
|
6
6
|
Author-email: Apache Software Foundation <dev@airflow.apache.org>
|
@@ -20,18 +20,17 @@ Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
22
22
|
Classifier: Topic :: System :: Monitoring
|
23
|
-
Requires-Dist: apache-airflow>=2.10.
|
23
|
+
Requires-Dist: apache-airflow>=2.10.0rc1
|
24
|
+
Requires-Dist: apache-airflow-providers-fab>=1.5.3rc1
|
24
25
|
Requires-Dist: pydantic>=2.11.0
|
25
26
|
Requires-Dist: retryhttp>=1.2.0,!=1.3.0
|
26
|
-
Requires-Dist: apache-airflow-providers-fab ; extra == "fab"
|
27
27
|
Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
|
28
|
-
Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.
|
29
|
-
Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.
|
28
|
+
Project-URL: Changelog, https://airflow.staged.apache.org/docs/apache-airflow-providers-edge3/1.1.0/changelog.html
|
29
|
+
Project-URL: Documentation, https://airflow.staged.apache.org/docs/apache-airflow-providers-edge3/1.1.0
|
30
30
|
Project-URL: Mastodon, https://fosstodon.org/@airflow
|
31
31
|
Project-URL: Slack Chat, https://s.apache.org/airflow-slack
|
32
32
|
Project-URL: Source Code, https://github.com/apache/airflow
|
33
33
|
Project-URL: YouTube, https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/
|
34
|
-
Provides-Extra: fab
|
35
34
|
|
36
35
|
|
37
36
|
.. Licensed to the Apache Software Foundation (ASF) under one
|
@@ -58,10 +57,22 @@ Provides-Extra: fab
|
|
58
57
|
|
59
58
|
Package ``apache-airflow-providers-edge3``
|
60
59
|
|
61
|
-
Release: ``1.
|
60
|
+
Release: ``1.1.0``
|
62
61
|
|
63
62
|
|
64
|
-
Handle edge workers on remote sites via HTTP(s) connection and orchestrates work over distributed sites
|
63
|
+
Handle edge workers on remote sites via HTTP(s) connection and orchestrates work over distributed sites.
|
64
|
+
|
65
|
+
When tasks need to be executed on remote sites where the connection need to pass through
|
66
|
+
firewalls or other network restrictions, the Edge Worker can be deployed. The Edge Worker
|
67
|
+
is a lightweight process with reduced dependencies. The worker only needs to be able to
|
68
|
+
communicate with the central Airflow site via HTTPS.
|
69
|
+
|
70
|
+
In the central Airflow site the EdgeExecutor is used to orchestrate the work. The EdgeExecutor
|
71
|
+
is a custom executor which is used to schedule tasks on the edge workers. The EdgeExecutor can co-exist
|
72
|
+
with other executors (for example CeleryExecutor or KubernetesExecutor) in the same Airflow site.
|
73
|
+
|
74
|
+
Additional REST API endpoints are provided to distribute tasks and manage the edge workers. The endpoints
|
75
|
+
are provided by the API server.
|
65
76
|
|
66
77
|
|
67
78
|
Provider package
|
@@ -71,7 +82,7 @@ This is a provider package for ``edge3`` provider. All classes for this provider
|
|
71
82
|
are in ``airflow.providers.edge3`` python package.
|
72
83
|
|
73
84
|
You can find package information and changelog for the provider
|
74
|
-
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.
|
85
|
+
in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.1.0/>`_.
|
75
86
|
|
76
87
|
Installation
|
77
88
|
------------
|
@@ -85,13 +96,14 @@ The package supports the following python versions: 3.9,3.10,3.11,3.12
|
|
85
96
|
Requirements
|
86
97
|
------------
|
87
98
|
|
88
|
-
|
89
|
-
PIP package
|
90
|
-
|
91
|
-
``apache-airflow``
|
92
|
-
``
|
93
|
-
``
|
94
|
-
|
99
|
+
================================ ===================
|
100
|
+
PIP package Version required
|
101
|
+
================================ ===================
|
102
|
+
``apache-airflow`` ``>=2.10.0``
|
103
|
+
``apache-airflow-providers-fab`` ``>=1.5.3``
|
104
|
+
``pydantic`` ``>=2.11.0``
|
105
|
+
``retryhttp`` ``>=1.2.0,!=1.3.0``
|
106
|
+
================================ ===================
|
95
107
|
|
96
108
|
Cross provider package dependencies
|
97
109
|
-----------------------------------
|
@@ -113,5 +125,5 @@ Dependent package
|
|
113
125
|
============================================================================================== =======
|
114
126
|
|
115
127
|
The changelog for the provider package can be found in the
|
116
|
-
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.
|
128
|
+
`changelog <https://airflow.apache.org/docs/apache-airflow-providers-edge3/1.1.0/changelog.html>`_.
|
117
129
|
|
@@ -1,21 +1,21 @@
|
|
1
1
|
airflow/providers/edge3/LICENSE,sha256=gXPVwptPlW1TJ4HSuG5OMPg-a3h43OGMkZRR1rpwfJA,10850
|
2
|
-
airflow/providers/edge3/__init__.py,sha256=
|
3
|
-
airflow/providers/edge3/get_provider_info.py,sha256=
|
4
|
-
airflow/providers/edge3/version_compat.py,sha256=
|
2
|
+
airflow/providers/edge3/__init__.py,sha256=NrV-3Uc00pr80NbPVUZWK9JYceO_jK26xp9BmvwFGnU,1494
|
3
|
+
airflow/providers/edge3/get_provider_info.py,sha256=Ek27-dB4UALHUFYoYjtoQIGq0p7zeHcEgmELHvpVmCU,6836
|
4
|
+
airflow/providers/edge3/version_compat.py,sha256=j5PCtXvZ71aBjixu-EFTNtVDPsngzzs7os0ZQDgFVDk,1536
|
5
5
|
airflow/providers/edge3/cli/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
6
|
-
airflow/providers/edge3/cli/api_client.py,sha256=
|
6
|
+
airflow/providers/edge3/cli/api_client.py,sha256=334KHVB4eMSzRpQ5emS56o-RTUJQprxf5Q3xQldCHDQ,7440
|
7
7
|
airflow/providers/edge3/cli/dataclasses.py,sha256=JUuvvmzSVWvG9uOEfzLIiXrTZ-HbESvu50jkPpVIYVw,2895
|
8
|
-
airflow/providers/edge3/cli/edge_command.py,sha256=
|
8
|
+
airflow/providers/edge3/cli/edge_command.py,sha256=dRKC1VPAUIvJnzvPlmcT98oBGlG0_MK7TBTNvjDEI2k,35658
|
9
9
|
airflow/providers/edge3/example_dags/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
10
|
-
airflow/providers/edge3/example_dags/integration_test.py,sha256=
|
10
|
+
airflow/providers/edge3/example_dags/integration_test.py,sha256=kevxwDePjTRqLWJGV-nKEQQWb7qb-y2oedm3mdqi6-8,6127
|
11
11
|
airflow/providers/edge3/example_dags/win_notepad.py,sha256=2evbqiupi5Ko4tyRkIEC5TPbc2c0wyR2YpsDYsBLMMM,2828
|
12
12
|
airflow/providers/edge3/example_dags/win_test.py,sha256=GegWqjvbsSdbsA_f3S9_FRYftVO0pggXwQQggB9Vvz4,13220
|
13
13
|
airflow/providers/edge3/executors/__init__.py,sha256=y830gGSKCvjOcLwLuCDp84NCrHWWB9RSSH1qvJpFhyY,923
|
14
|
-
airflow/providers/edge3/executors/edge_executor.py,sha256=
|
14
|
+
airflow/providers/edge3/executors/edge_executor.py,sha256=Z7q1Ph_SvAuT1cR4YYcEmKYt4Yi711q65VrzofQ5t1A,15791
|
15
15
|
airflow/providers/edge3/models/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
16
16
|
airflow/providers/edge3/models/edge_job.py,sha256=rdl9cH1bBrc7id8zkZ7uxsCJNPLG-8o9cnspGwBPBcQ,3167
|
17
17
|
airflow/providers/edge3/models/edge_logs.py,sha256=bNstp7gR54O2vbxzz4NTL0erbifFbGUjZ-YOM0I4sqk,2768
|
18
|
-
airflow/providers/edge3/models/edge_worker.py,sha256=
|
18
|
+
airflow/providers/edge3/models/edge_worker.py,sha256=7U74k24xw36X8gQkrac3yWbtFBHbopvmRIWsjpFY8Og,10693
|
19
19
|
airflow/providers/edge3/openapi/__init__.py,sha256=0O-WvmDx8GeKSoECpHYrbe0hW-LgjlKny3VqTCpBQeQ,927
|
20
20
|
airflow/providers/edge3/openapi/edge_worker_api_v1.yaml,sha256=GAE2IdOXmcUueNy5KFkLBgNpoWnOjnHT9TrW5NZEWpI,24938
|
21
21
|
airflow/providers/edge3/plugins/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
@@ -24,7 +24,7 @@ airflow/providers/edge3/plugins/templates/edge_worker_hosts.html,sha256=0_P2yfZw
|
|
24
24
|
airflow/providers/edge3/plugins/templates/edge_worker_jobs.html,sha256=bZ-6ysmIy6j4eR_TPHiqbgb3qpNMKCcEEB-SpxuxNgc,2831
|
25
25
|
airflow/providers/edge3/worker_api/__init__.py,sha256=nnPvxWGTEKZ9YyB1Yd7P9IvDOenK01LVHm22Owwxj3g,839
|
26
26
|
airflow/providers/edge3/worker_api/app.py,sha256=Dda2VjkzgBtbQbSWSVEAoqd22RlqvBMyiPau65uKkv4,2006
|
27
|
-
airflow/providers/edge3/worker_api/auth.py,sha256=
|
27
|
+
airflow/providers/edge3/worker_api/auth.py,sha256=XVTfL-c0JYUhpVkKdqqaxZACZXqvnPp_3W6q2TK0Bjc,4883
|
28
28
|
airflow/providers/edge3/worker_api/datamodels.py,sha256=FAiXqnrSN8zH4YE2fUMjXfXcH9cHlhRh4uZvvr936Ys,6696
|
29
29
|
airflow/providers/edge3/worker_api/routes/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
30
30
|
airflow/providers/edge3/worker_api/routes/_v2_compat.py,sha256=Q4b2Io0yoK5V_hbgk6fiFviTeT6CFbFMOGYRZgLEeR4,4543
|
@@ -32,8 +32,8 @@ airflow/providers/edge3/worker_api/routes/_v2_routes.py,sha256=-WAofvXJpOYpTyh98
|
|
32
32
|
airflow/providers/edge3/worker_api/routes/health.py,sha256=XxqIppnRA138Q6mAHCdyL2JvoeeganUiI-TXyXSPTGo,1075
|
33
33
|
airflow/providers/edge3/worker_api/routes/jobs.py,sha256=UK1w6nXEUadOLwE9abZ4jHH4KtbvXcwaAF0EnwSa3y4,5733
|
34
34
|
airflow/providers/edge3/worker_api/routes/logs.py,sha256=uk0SZ5hAimj3sAcq1FYCDu0AXYNeTeyjZDGBvw-986E,4945
|
35
|
-
airflow/providers/edge3/worker_api/routes/worker.py,sha256=
|
36
|
-
apache_airflow_providers_edge3-1.
|
37
|
-
apache_airflow_providers_edge3-1.
|
38
|
-
apache_airflow_providers_edge3-1.
|
39
|
-
apache_airflow_providers_edge3-1.
|
35
|
+
airflow/providers/edge3/worker_api/routes/worker.py,sha256=BGARu1RZ74lW9X-ltuMYbbVXczm_MZdqHaai2MhDWtY,8969
|
36
|
+
apache_airflow_providers_edge3-1.1.0rc1.dist-info/entry_points.txt,sha256=7WUIGfd3o9NvvbK5trbZxNXTgYGc6pqg74wZPigbx5o,206
|
37
|
+
apache_airflow_providers_edge3-1.1.0rc1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
38
|
+
apache_airflow_providers_edge3-1.1.0rc1.dist-info/METADATA,sha256=3kJ3dEUkqjhppUJvNuD0I4r7MOg0pZ1kjmWcOrOARS0,5876
|
39
|
+
apache_airflow_providers_edge3-1.1.0rc1.dist-info/RECORD,,
|
File without changes
|