Skip to content

Commit

Permalink
chore: release 1.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
Panaetius authored Mar 28, 2022
2 parents cb1ed39 + a8c4c61 commit 887cf46
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 7 deletions.
11 changes: 11 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@
Changes
=======

`1.1.4 <https://github.com/SwissDataScienceCenter/renku-python/compare/v1.1.3...v1.1.4>`__ (2022-03-28)
-------------------------------------------------------------------------------------------------------

This is a bugfix release fixing an issue with cycle detection in workflows.

Bug Fixes
~~~~~~~~~

- **core:** prevent creating cycles when creating/executing workflows. Fix color in `workflow visualize`.
(`#2785 <https://github.com/SwissDataScienceCenter/renku-python/pull/2785>`__)

`1.1.3 <https://github.com/SwissDataScienceCenter/renku-python/compare/v1.1.2...v1.1.3>`__ (2022-03-25)
-------------------------------------------------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion helm-chart/renku-core/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ appVersion: "1.0"
description: A Helm chart for Kubernetes
name: renku-core
icon: https://avatars0.githubusercontent.com/u/53332360?s=400&u=a4311d22842343604ef61a8c8a1e5793209a67e9&v=4
version: 1.1.3
version: 1.1.4
2 changes: 1 addition & 1 deletion helm-chart/renku-core/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ versions:
fullnameOverride: ""
image:
repository: renku/renku-core
tag: "v1.1.3"
tag: "v1.1.4"
pullPolicy: IfNotPresent
v8:
name: v8
Expand Down
2 changes: 1 addition & 1 deletion renku/core/commands/view_model/activity_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def _add_edges_to_canvas(

colors = {e.color for e in intersecting_edges}

if len(colors) < len(EdgeShape.EDGE_COLORS):
if len(colors) < len(EdgeShape.COLORS):
while edge_color in colors:
edge_color = EdgeShape.next_color()
for e in new_edges:
Expand Down
8 changes: 7 additions & 1 deletion renku/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ class GitCommandError(GitError):
"""Raised when a Git command fails."""

def __init__(self, message="Git command failed.", command=None, stdout=None, stderr=None, status=None):
"""Build a custom message."""
super().__init__(message)
self.command = command
self.stdout = stdout
Expand Down Expand Up @@ -551,7 +552,12 @@ class GraphCycleError(RenkuException):
def __init__(self, cycles: List[List[str]]):
"""Embed exception and build a custom message."""
cycles = "), (".join(", ".join(cycle) for cycle in cycles)
super().__init__(f"Cycles detected in execution graph: ({cycles})")
super().__init__(
f"Cycles detected in execution graph: ({cycles})\nCircular workflows are not supported in renku\n"
"If this happened as part of a 'renku run' or 'renku workflow execute', please git reset and clean"
"the project and try again. This might be due to renku erroneously detecting an input as an output, "
"if so, please specify the wrongly detected output as an explicit input using '--input'."
)


class NothingToExecuteError(RenkuException):
Expand Down
9 changes: 6 additions & 3 deletions renku/core/management/workflow/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,11 @@ def remove_overridden_activities():

connect_nodes_based_on_dependencies()

if not networkx.algorithms.dag.is_directed_acyclic_graph(graph):
raise ValueError("Cannot find execution order: Project has cyclic dependencies.")
cycles = list(networkx.algorithms.cycles.simple_cycles(graph))

if cycles:
cycles = [map(lambda x: getattr(x, "id", x), cycle) for cycle in cycles]
raise errors.GraphCycleError(cycles)

connect_nodes_by_execution_order()
remove_overridden_activities()
Expand All @@ -215,7 +218,7 @@ def remove_overridden_activities():


def sort_activities(activities: List[Activity], remove_overridden_parents=True) -> List[Activity]:
"""Returns a sorted list of activities based on their dependencies and execution order."""
"""Return a sorted list of activities based on their dependencies and execution order."""
graph = create_activity_graph(activities, remove_overridden_parents)

return list(networkx.topological_sort(graph))
15 changes: 15 additions & 0 deletions renku/core/metadata/gateway/activity_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# limitations under the License.
"""Renku activity database gateway implementation."""

from itertools import chain
from pathlib import Path
from typing import List, Optional, Set, Tuple, Union

Expand All @@ -26,6 +27,7 @@
from renku.core.management.interface.activity_gateway import IActivityGateway
from renku.core.management.interface.database_dispatcher import IDatabaseDispatcher
from renku.core.management.interface.plan_gateway import IPlanGateway
from renku.core.management.workflow.activity import create_activity_graph
from renku.core.metadata.gateway.database_gateway import ActivityDownstreamRelation
from renku.core.models.provenance.activity import Activity, ActivityCollection
from renku.core.models.workflow.plan import Plan
Expand Down Expand Up @@ -148,6 +150,19 @@ def add(self, activity: Activity):
plan_gateway = inject.instance(IPlanGateway)
plan_gateway.add(activity.association.plan)

# NOTE: Check for a cycle if this activity
upstream_chains = self.get_upstream_activity_chains(activity)
downstream_chains = self.get_downstream_activity_chains(activity)

all_activities = set()

for activity_chain in chain(upstream_chains, downstream_chains):
for current_activity in activity_chain:
all_activities.add(current_activity)

# NOTE: This call raises an exception if there is a cycle
create_activity_graph(list(all_activities), with_inputs_outputs=True)

def add_activity_collection(self, activity_collection: ActivityCollection):
"""Add an ``ActivityCollection`` to storage."""
database = self.database_dispatcher.current_database
Expand Down
32 changes: 32 additions & 0 deletions tests/cli/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,3 +902,35 @@ def test_workflow_iterate(runner, run_shell, client, workflow, parameters, provi
# check whether parameters setting was effective
for o in outputs:
assert Path(o).resolve().exists()


def test_workflow_cycle_detection(run_shell, project, capsys, client):
"""Test creating a cycle is not possible with renku run or workflow execute."""
input = client.path / "input"

with client.commit():
input.write_text("test")

result = run_shell("renku run --name run1 -- cp input output")

# Assert expected empty stdout.
assert b"" == result[0]
# Assert not allocated stderr.
assert result[1] is None

result = run_shell("renku run --name run2 -- wc output > input")

assert b"Cycles detected in execution graph" in result[0]

run_shell("git clean -fd && git reset --hard")

result = run_shell("renku run --name run2 -- wc output > wordcount")

# Assert expected empty stdout.
assert b"" == result[0]
# Assert not allocated stderr.
assert result[1] is None

result = run_shell("renku workflow execute --set output-2=input run2")

assert b"Cycles detected in execution graph" in result[0]

0 comments on commit 887cf46

Please sign in to comment.