From 84770b9ef2afbfa1232845ce6538b56df99cee3a Mon Sep 17 00:00:00 2001
From: Steven Armstrong <steven@icarus.ethz.ch>
Date: Mon, 19 Nov 2012 11:25:56 +0100
Subject: [PATCH] implement before/after to declare dependencies and deprecate
 require

Signed-off-by: Steven Armstrong <steven@icarus.ethz.ch>

--whitespace

Signed-off-by: Steven Armstrong <steven@icarus.ethz.ch>
---
 cdist/core/cdist_object.py    |  2 ++
 cdist/emulator.py             | 25 +++++++++++++++++++++----
 cdist/resolver.py             | 17 ++++++++++++++---
 cdist/test/object/__init__.py |  1 +
 4 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/cdist/core/cdist_object.py b/cdist/core/cdist_object.py
index 90a21e59..7e587bf3 100644
--- a/cdist/core/cdist_object.py
+++ b/cdist/core/cdist_object.py
@@ -186,6 +186,8 @@ class CdistObject(object):
         return os.path.join(self.path, "explorer")
 
     requirements = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'require'))
+    before = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'before'))
+    after = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'after'))
     autorequire = fsproperty.FileListProperty(lambda obj: os.path.join(obj.absolute_path, 'autorequire'))
     parameters = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.parameter_path))
     explorers = fsproperty.DirectoryDictProperty(lambda obj: os.path.join(obj.base_path, obj.explorer_path))
diff --git a/cdist/emulator.py b/cdist/emulator.py
index a9c760cb..bd3661d3 100644
--- a/cdist/emulator.py
+++ b/cdist/emulator.py
@@ -23,6 +23,7 @@
 import argparse
 import logging
 import os
+import warnings
 import sys
 
 import cdist
@@ -92,8 +93,12 @@ class Emulator(object):
 
     def commandline(self):
         """Parse command line"""
+        self.meta_parameters = dict.fromkeys(('after', 'before'))
+        meta_parser = argparse.ArgumentParser(add_help=False)
+        for meta_parameter in self.meta_parameters.keys():
+            meta_parser.add_argument('--%s' % meta_parameter, action='append', required=False)
 
-        parser = argparse.ArgumentParser(add_help=False, argument_default=argparse.SUPPRESS)
+        parser = argparse.ArgumentParser(add_help=False, parents=[meta_parser], argument_default=argparse.SUPPRESS)
 
         for parameter in self.cdist_type.required_parameters:
             argument = "--" + parameter
@@ -119,6 +124,11 @@ class Emulator(object):
         self.args = parser.parse_args(self.argv[1:])
         self.log.debug('Args: %s' % self.args)
 
+        # Handle meta parameters
+        for meta_parameter in self.meta_parameters.keys():
+            if meta_parameter in self.args:
+                self.meta_parameters[meta_parameter] = getattr(self.args, meta_parameter)
+                delattr(self.args, meta_parameter)
 
     def setup_object(self):
         # Setup object_id - FIXME: unset / do not setup anymore!
@@ -175,10 +185,18 @@ class Emulator(object):
 
     def record_requirements(self):
         """record requirements"""
+        for key in ('before', 'after'):
+            if key in self.meta_parameters and self.meta_parameters[key]:
+                for value in self.meta_parameters[key]:
+                    self.log.debug("Recording requirement: %s %s %s", self.cdist_object.name, key, value)
+                    dependency_list = getattr(self.cdist_object, key)
+                    # append to the object.after or object.before lists
+                    dependency_list.append(value)
 
         if "require" in self.env:
+            warnings.warn("The 'require' envrionment variable is deprecated. Use the --before and --after meta parameters to define dependencies.", category=PendingDeprecationWarning, stacklevel=2)
+
             requirements = self.env['require']
-            self.log.debug("reqs = " + requirements)
             for requirement in requirements.split(" "):
                 # Ignore empty fields - probably the only field anyway
                 if len(requirement) == 0: continue
@@ -187,11 +205,10 @@ class Emulator(object):
                 cdist_object = self.cdist_object.object_from_name(requirement)
 
                 self.log.debug("Recording requirement: " + requirement)
-
                 # Save the sanitised version, not the user supplied one
                 # (__file//bar => __file/bar)
                 # This ensures pattern matching is done against sanitised list
-                self.cdist_object.requirements.append(cdist_object.name)
+                self.cdist_object.after.append(cdist_object.name)
 
     def record_auto_requirements(self):
         """An object shall automatically depend on all objects that it defined in it's type manifest.
diff --git a/cdist/resolver.py b/cdist/resolver.py
index 7e3a1a68..d181d066 100644
--- a/cdist/resolver.py
+++ b/cdist/resolver.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2011-2012 Steven Armstrong (steven-cdist at armstrong.cc)
 #
 # This file is part of cdist.
 #
@@ -109,10 +109,21 @@ class DependencyResolver(object):
                     raise RequirementNotFoundError(pattern)
 
     def _preprocess_requirements(self):
-        """Find all autorequire dependencies and merge them to be just requirements
-        for further processing.
+        """Find all before, after and autorequire dependencies and merge them
+        to be just requirements for further processing.
         """
         for cdist_object in self.objects.values():
+            if cdist_object.after:
+                cdist_object.requirements.extend(cdist_object.after)
+                # As we changed the object on disc, we have to ensure it is not 
+                # preprocessed again if someone would call us multiple times.
+                cdist_object.after = []
+            if cdist_object.before:
+                for other_object in self.find_requirements_by_name(cdist_object.before):
+                    other_object.requirements.append(cdist_object.name)
+                # As we changed the object on disc, we have to ensure it is not 
+                # preprocessed again if someone would call us multiple times.
+                cdist_object.before = []
             if cdist_object.autorequire:
                 # The objects (children) that this cdist_object (parent) defined
                 # in it's type manifest shall inherit all explicit requirements 
diff --git a/cdist/test/object/__init__.py b/cdist/test/object/__init__.py
index 3a91f709..7bdc037e 100644
--- a/cdist/test/object/__init__.py
+++ b/cdist/test/object/__init__.py
@@ -87,6 +87,7 @@ class ObjectTestCase(test.CdistTestCase):
         self.cdist_object.code_local = ''
         self.cdist_object.code_remote = ''
         self.cdist_object.state = ''
+        self.cdist_object.requirements = []
 
     def test_name(self):
         self.assertEqual(self.cdist_object.name, '__third/moon')