11#!/usr/bin/env python3
22
33import click
4+ import datetime
45import json
56import sys
67import time
1011from flytekit .remote import FlyteRemote
1112from flytekit .models .core .execution import WorkflowExecutionPhase
1213from flytekit .configuration import Config , ImageConfig , SerializationSettings
14+ from flytekit .remote .executions import FlyteWorkflowExecution
1315
1416
1517WAIT_TIME = 10
16- MAX_ATTEMPTS = 60
18+ MAX_ATTEMPTS = 200
1719
1820# This dictionary maps the names found in the flytesnacks manifest to a list of workflow names and
1921# inputs. This is so we can progressively cover all priorities in the original flytesnacks manifest,
2022# starting with "core".
2123FLYTESNACKS_WORKFLOW_GROUPS : Mapping [str , List [Tuple [str , dict ]]] = {
24+ "lite" : [
25+ ("basics.hello_world.my_wf" , {}),
26+ ("basics.lp.go_greet" , {"day_of_week" : "5" , "number" : 3 , "am" : True }),
27+ ],
2228 "core" : [
23- ("core.control_flow.chain_tasks.chain_tasks_wf" , {}),
24- ("core.control_flow.dynamics.wf" , {"s1" : "Pear" , "s2" : "Earth" }),
25- ("core.control_flow.map_task.my_map_workflow" , {"a" : [1 , 2 , 3 , 4 , 5 ]}),
29+ ("basics.deck.wf" , {}),
30+ # The chain_workflows example in flytesnacks expects to be running in a sandbox.
31+ # ("control_flow.chain_entities.chain_workflows_wf", {}),
32+ ("control_flow.dynamics.wf" , {"s1" : "Pear" , "s2" : "Earth" }),
33+ ("control_flow.map_task.my_map_workflow" , {"a" : [1 , 2 , 3 , 4 , 5 ]}),
2634 # Workflows that use nested executions cannot be launched via flyteremote.
2735 # This issue is being tracked in https://github.com/flyteorg/flyte/issues/1482.
28- # ("core. control_flow.run_conditions.multiplier", {"my_input": 0.5}),
29- # ("core. control_flow.run_conditions.multiplier_2", {"my_input": 10}),
30- # ("core. control_flow.run_conditions.multiplier_3", {"my_input": 5}),
31- # ("core. control_flow.run_conditions.basic_boolean_wf", {"seed": 5}),
32- # ("core. control_flow.run_conditions.bool_input_wf", {"b": True}),
33- # ("core. control_flow.run_conditions.nested_conditions", {"my_input": 0.4}),
34- # ("core. control_flow.run_conditions.consume_outputs", {"my_input": 0.4, "seed": 7}),
35- # ("core. control_flow.run_merge_sort.merge_sort", {"numbers": [5, 4, 3, 2, 1], "count": 5}),
36- ("core. control_flow.subworkflows.parent_wf" , {"a" : 3 }),
37- ("core. control_flow.subworkflows.nested_parent_wf" , {"a" : 3 }),
38- ("core.flyte_basics .basic_workflow.my_wf" , {"a" : 50 , "b" : "hello" }),
36+ # ("control_flow.run_conditions.multiplier", {"my_input": 0.5}),
37+ # ("control_flow.run_conditions.multiplier_2", {"my_input": 10}),
38+ # ("control_flow.run_conditions.multiplier_3", {"my_input": 5}),
39+ # ("control_flow.run_conditions.basic_boolean_wf", {"seed": 5}),
40+ # ("control_flow.run_conditions.bool_input_wf", {"b": True}),
41+ # ("control_flow.run_conditions.nested_conditions", {"my_input": 0.4}),
42+ # ("control_flow.run_conditions.consume_outputs", {"my_input": 0.4, "seed": 7}),
43+ # ("control_flow.run_merge_sort.merge_sort", {"numbers": [5, 4, 3, 2, 1], "count": 5}),
44+ ("control_flow.subworkflows.parent_wf" , {"a" : 3 }),
45+ ("control_flow.subworkflows.nested_parent_wf" , {"a" : 3 }),
46+ ("basics .basic_workflow.my_wf" , {"a" : 50 , "b" : "hello" }),
3947 # TODO: enable new files and folders workflows
40- # ("core.flyte_basics .files.rotate_one_workflow", {"in_image": "https://upload.wikimedia.org/wikipedia/commons/d/d2/Julia_set_%28C_%3D_0.285%2C_0.01%29.jpg"}),
41- # ("core.flyte_basics .folders.download_and_rotate", {}),
42- ("core.flyte_basics .hello_world.my_wf" , {}),
43- ("core.flyte_basics .lp.my_wf" , {"val" : 4 }),
44- ("core.flyte_basics .lp.go_greet" , {"day_of_week" : "5" , "number" : 3 , "am" : True }),
45- ("core.flyte_basics .named_outputs.my_wf" , {}),
48+ # ("basics .files.rotate_one_workflow", {"in_image": "https://upload.wikimedia.org/wikipedia/commons/d/d2/Julia_set_%28C_%3D_0.285%2C_0.01%29.jpg"}),
49+ # ("basics .folders.download_and_rotate", {}),
50+ ("basics .hello_world.my_wf" , {}),
51+ ("basics .lp.my_wf" , {"val" : 4 }),
52+ ("basics .lp.go_greet" , {"day_of_week" : "5" , "number" : 3 , "am" : True }),
53+ ("basics .named_outputs.my_wf" , {}),
4654 # # Getting a 403 for the wikipedia image
47- # # ("core.flyte_basics .reference_task.wf", {}),
48- ("core. type_system.custom_objects.wf" , {"x" : 10 , "y" : 20 }),
55+ # # ("basics .reference_task.wf", {}),
56+ ("type_system.custom_objects.wf" , {"x" : 10 , "y" : 20 }),
4957 # Enums are not supported in flyteremote
50- # ("core.type_system.enums.enum_wf", {"c": "red"}),
51- ("core.type_system.schema.df_wf" , {"a" : 42 }),
52- ("core.type_system.typed_schema.wf" , {}),
53- ("my.imperative.workflow.example" , {"in1" : "hello" , "in2" : "foo" }),
58+ # ("type_system.enums.enum_wf", {"c": "red"}),
59+ ("type_system.schema.df_wf" , {"a" : 42 }),
60+ ("type_system.typed_schema.wf" , {}),
61+ #("my.imperative.workflow.example", {"in1": "hello", "in2": "foo"}),
62+ ],
63+ "integrations-k8s-spark" : [
64+ ("k8s_spark.pyspark_pi.my_spark" , {"triggered_date" : datetime .datetime .now ()}),
65+ ],
66+ "integrations-kfpytorch" : [
67+ ("kfpytorch.pytorch_mnist.pytorch_training_wf" , {}),
68+ ],
69+ "integrations-kftensorflow" : [
70+ ("kftensorflow.tf_mnist.mnist_tensorflow_workflow" , {}),
71+ ],
72+ # "integrations-pod": [
73+ # ("pod.pod.pod_workflow", {}),
74+ # ],
75+ "integrations-pandera_examples" : [
76+ ("pandera_examples.basic_schema_example.process_data" , {}),
77+ # TODO: investigate type mismatch float -> numpy.float64
78+ # ("pandera_examples.validating_and_testing_ml_pipelines.pipeline", {"data_random_state": 42, "model_random_state": 99}),
79+ ],
80+ "integrations-modin_examples" : [
81+ ("modin_examples.knn_classifier.pipeline" , {}),
82+ ],
83+ "integrations-papermilltasks" : [
84+ ("papermilltasks.simple.nb_to_python_wf" , {"f" : 3.1415926535 }),
85+ ],
86+ "integrations-greatexpectations" : [
87+ ("greatexpectations.task_example.simple_wf" , {}),
88+ ("greatexpectations.task_example.file_wf" , {}),
89+ ("greatexpectations.task_example.schema_wf" , {}),
90+ ("greatexpectations.task_example.runtime_wf" , {}),
5491 ],
5592}
5693
5794
58- def run_launch_plan (remote , version , workflow_name , inputs ):
95+ def execute_workflow (remote , version , workflow_name , inputs ):
5996 print (f"Fetching workflow={ workflow_name } and version={ version } " )
60- lp = remote .fetch_workflow (name = workflow_name , version = version )
61- return remote .execute (lp , inputs = inputs , wait = False )
97+ wf = remote .fetch_workflow (name = workflow_name , version = version )
98+ return remote .execute (wf , inputs = inputs , wait = False )
99+
100+ def executions_finished (executions_by_wfgroup : Dict [str , List [FlyteWorkflowExecution ]]) -> bool :
101+ for executions in executions_by_wfgroup .values ():
102+ if not all ([execution .is_done for execution in executions ]):
103+ return False
104+ return True
62105
106+ def sync_executions (remote : FlyteRemote , executions_by_wfgroup : Dict [str , List [FlyteWorkflowExecution ]]):
107+ try :
108+ for executions in executions_by_wfgroup .values ():
109+ for execution in executions :
110+ print (f"About to sync execution_id={ execution .id .name } " )
111+ remote .sync (execution )
112+ except :
113+ print ("GOT TO THE EXCEPT" )
114+ print ("COUNT THIS!" )
63115
64- def schedule_workflow_group (
116+
117+ def report_executions (executions_by_wfgroup : Dict [str , List [FlyteWorkflowExecution ]]):
118+ for executions in executions_by_wfgroup .values ():
119+ for execution in executions :
120+ print (execution )
121+
122+ def schedule_workflow_groups (
65123 tag : str ,
66- workflow_group : str ,
124+ workflow_groups : List [ str ] ,
67125 remote : FlyteRemote ,
68126 terminate_workflow_on_failure : bool ,
69- ) -> bool :
127+ ) -> Dict [ str , bool ] :
70128 """
71- Schedule all workflows executions and return True if all executions succeed, otherwise
129+ Schedule workflows executions for all workflow gropus and return True if all executions succeed, otherwise
72130 return False.
73131 """
74- workflows = FLYTESNACKS_WORKFLOW_GROUPS .get (workflow_group , [])
75-
76- launch_plans = [
77- run_launch_plan (remote , tag , workflow [0 ], workflow [1 ]) for workflow in workflows
78- ]
132+ executions_by_wfgroup = {}
133+ # Schedule executions for each workflow group,
134+ for wf_group in workflow_groups :
135+ workflows = FLYTESNACKS_WORKFLOW_GROUPS .get (wf_group , [])
136+ executions_by_wfgroup [wf_group ] = [
137+ execute_workflow (remote , tag , workflow [0 ], workflow [1 ]) for workflow in workflows
138+ ]
79139
80- # Wait for all launch plans to finish
140+ # Wait for all executions to finish
81141 attempt = 0
82142 while attempt == 0 or (
83- not all ([ lp . is_done for lp in launch_plans ] ) and attempt < MAX_ATTEMPTS
143+ not executions_finished ( executions_by_wfgroup ) and attempt < MAX_ATTEMPTS
84144 ):
85145 attempt += 1
86146 print (
87147 f"Not all executions finished yet. Sleeping for some time, will check again in { WAIT_TIME } s"
88148 )
89149 time .sleep (WAIT_TIME )
90- # Need to sync to refresh status of executions
91- for lp in launch_plans :
92- print (f"About to sync execution_id={ lp .id .name } " )
93- remote .sync (lp )
94-
95- # Report result of each launch plan
96- for lp in launch_plans :
97- print (lp )
98-
99- # Collect all failing launch plans
100- non_succeeded_lps = [
101- lp
102- for lp in launch_plans
103- if lp .closure .phase != WorkflowExecutionPhase .SUCCEEDED
104- ]
105-
106- if len (non_succeeded_lps ) == 0 :
107- print ("All executions succeeded." )
108- return True
109-
110- print ("Failed executions:" )
111- # Report failing cases
112- for lp in non_succeeded_lps :
113- print (f" workflow={ lp .spec .launch_plan .name } , execution_id={ lp .id .name } " )
114- if terminate_workflow_on_failure :
115- remote .terminate (lp , "aborting execution scheduled in functional test" )
116- return False
150+ sync_executions (remote , executions_by_wfgroup )
151+
152+
153+ report_executions (executions_by_wfgroup )
154+
155+ results = {}
156+ for wf_group , executions in executions_by_wfgroup .items ():
157+ non_succeeded_executions = []
158+ for execution in executions :
159+ if execution .closure .phase != WorkflowExecutionPhase .SUCCEEDED :
160+ non_succeeded_executions .append (execution )
161+ # Report failing cases
162+ if len (non_succeeded_executions ) != 0 :
163+ print (f"Failed executions for { wf_group } :" )
164+ for execution in non_succeeded_executions :
165+ print (f" workflow={ execution .spec .launch_plan .name } , execution_id={ execution .id .name } " )
166+ if terminate_workflow_on_failure :
167+ remote .terminate (execution , "aborting execution scheduled in functional test" )
168+ # A workflow group succeeds iff all of its executions succeed
169+ results [wf_group ] = len (non_succeeded_executions ) == 0
170+ return results
117171
118172
119173def valid (workflow_group ):
@@ -139,14 +193,16 @@ def run(
139193 # For a given release tag and priority, this function filters the workflow groups from the flytesnacks
140194 # manifest file. For example, for the release tag "v0.2.224" and the priority "P0" it returns [ "core" ].
141195 manifest_url = "https://raw.githubusercontent.com/flyteorg/flytesnacks/" \
142- f"{ flytesnacks_release_tag } /cookbook/ flyte_tests_manifest.json"
196+ f"{ flytesnacks_release_tag } /flyte_tests_manifest.json"
143197 r = requests .get (manifest_url )
144198 parsed_manifest = r .json ()
199+ workflow_groups = []
200+ workflow_groups = ["lite" ] if "lite" in priorities else [
201+ group ["name" ] for group in parsed_manifest if group ["priority" ] in priorities
202+ ]
145203
146- workflow_groups = [
147- group ["name" ] for group in parsed_manifest if group ["priority" ] in priorities
148- ]
149204 results = []
205+ valid_workgroups = []
150206 for workflow_group in workflow_groups :
151207 if not valid (workflow_group ):
152208 results .append (
@@ -157,19 +213,17 @@ def run(
157213 }
158214 )
159215 continue
216+ valid_workgroups .append (workflow_group )
160217
161- try :
162- workflows_succeeded = schedule_workflow_group (
163- flytesnacks_release_tag ,
164- workflow_group ,
165- remote ,
166- terminate_workflow_on_failure ,
167- )
168- except Exception :
169- print (traceback .format_exc ())
170- workflows_succeeded = False
218+ results_by_wfgroup = schedule_workflow_groups (
219+ flytesnacks_release_tag ,
220+ valid_workgroups ,
221+ remote ,
222+ terminate_workflow_on_failure
223+ )
171224
172- if workflows_succeeded :
225+ for workflow_group , succeeded in results_by_wfgroup .items ():
226+ if succeeded :
173227 background_color = "green"
174228 status = "passing"
175229 else :
0 commit comments