8000 DAG by ejholmes · Pull Request #280 · cloudtools/stacker · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

DAG #280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed

DAG #280

wants to merge 12 commits into from

Conversation

ejholmes
Copy link
Contributor
@ejholmes ejholmes commented Dec 1, 2016

This is an internal refactor of stacker.plan.Plan to use a DAG to internally represent steps and their dependencies. The net result provides a simpler method to walk the graph, and update stacks in order of their dependencies.

Currently, this doesn't walk the graph in parallel, which is implemented in #281.

The DAG implementation is pulled from https://github.com/thieman/py-dag with the addition of a walk method for walking the graph in topological order.

TODO

  • Better reverse walk.

@ejholmes
Copy link
Contributor Author
ejholmes commented Dec 1, 2016

Ran this against our staging environment without issue. Gonna take a look at adding a walk_parallel method to the DAG.

Copy link
Member
@phobologic phobologic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! A few comments - I'll look at this again soon, couldn't do a full review while at re:Invent.

""" Add an edge (dependency) between the specified nodes. """
if not graph:
graph = self.graph
if ind_node not in graph or dep_node not in graph:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth it to indicate which node wasn't in the graph.


def __init__(self, description, sleep_time=5, wait_func=None,
watch_func=None, *args, **kwargs):
class Plan():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be class Plan(object):

def __init__(self, description, sleep_time=5, wait_func=None,
watch_func=None, *args, **kwargs):
class Plan():
def __init__(self, description, reverse=False, sleep_func=sleep):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth adding back the doc string here.

Returns:
list: A list of :class:`Step` objects that are in the given status.
def build(self, stacks):
""" Builds an internal dag from the stacks and their dependencies """
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the full docstring here for the API docs.

try:
dag.add_edge(stack.fqn, dep)
except DAGValidationError:
raise CyclicDependencyError(stack.fqn)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add the dependency to this exception as well, just so it's easier to see what dependency caused the error.

raise CyclicDependencyError(stack.fqn)

self._dag = dag
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to do this -

>>> def blah():
...   print "WOO"
...
>>>
>>> x = blah()
WOO
>>> x
>>> type(x)
<type 'NoneType'>

if not graph:
graph = self.graph
for node, edges in graph.items():

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird blank line here.

10000

def validate(self, graph=None):
""" Returns (Boolean, message) of whether DAG is valid. """
graph = graph if graph is not None else self.graph
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different than the way it's done throughout this class - might be worth changing it just for consistency.

@ejholmes
Copy link
Contributor Author
ejholmes commented Dec 2, 2016

Ok. Think I got all the changes in based on feedback.

We should probably hold off on merging until #281 is ready, since this will be slower to start without a parallel walk.

if not graph:
graph = self.graph
nodes = self.topological_sort(graph=graph)
if reverse:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is kinda a naive implementation for reversing a dag. Doing this, we can't walk the graph concurrently in reverse. Instead, we should do a depth first walk, and build a new graph with the edges reversed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a commit that adds a tranpose method on DAG to build a new DAG with all the edges transposed. Plan uses this now for destroy actions, so it can be parallelized.

@@ -271,26 +271,10 @@ def _handle_missing_parameters(self, params, required_params,
return params.items()

def _generate_plan(self, tail=False):
plan_kwargs = {}
if tail:
plan_kwargs["watch_func"] = self.provider.tail_stack
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets removed in this PR, which may not be correct. This seems to tail the events, but I'm not sure I've ever actually seen stacker do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - this is used by some people with --tail. Does it cause issues if you leave it in?

Copy link
Member
@phobologic phobologic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small changes, but looks good to me!

context=self.context)

plan = Plan(description="Test", sleep_func=None)

with self.assertRaises(CyclicDependencyError):
plan.build([vpc, bastion])
try:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you want here is self.assertRaises(). You can see an example of that here:

https://github.com/remind101/stacker/blob/6a141b6d9c429d89158ad7ea274cdfcfbdc6060d/stacker/tests/test_config.py#L15

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sweet. I couldn't find an example online that used assertRaises to check the message. Much nicer.

@phobologic
Copy link
Member

Oh, also, once you submit this, could you open another PR to merge this against the release-1.0 branch?

@ejholmes
Copy link
Contributor Author

Alright, this has gotten behind master which is moving towards 1.0, and my original version of this was based on top of 0.8.5. I've updated this for 0.8.6, if anybody wants to use it (dag-0.8.6 branch, much faster than 0.8.6), but it won't be merged into 0.8 stable.

I'll follow up with porting this forward to 1.0 once we've moved internally to 1.0.

@ejholmes ejholmes closed this Mar 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0