From c0b8edbf9c7be0c855a607fa14b6a4bec477bc80 Mon Sep 17 00:00:00 2001 From: Jae Kun Choi Date: Mon, 22 May 2017 23:52:53 +1000 Subject: [PATCH 01/14] Now Ref function is supported inside userdata --- troposphere/helpers/userdata.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py index 5d25d1366..017b5ab74 100644 --- a/troposphere/helpers/userdata.py +++ b/troposphere/helpers/userdata.py @@ -6,6 +6,7 @@ def from_file(filepath, delimiter='', blanklines=False): """ Imports userdata from a file. + Also supports passing Troposphere Ref function :type filepath: string :param filepath @@ -23,16 +24,30 @@ def from_file(filepath, delimiter='', blanklines=False): :return The base64 representation of the file. """ + ref_pattern = re.compile('(?P.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])') data = [] try: with open(filepath, 'r') as f: for line in f: + print line if blanklines and line.strip('\n\r ') == '': continue - data.append(line) + ref_ex = ref_pattern.match(line) + + if ref_ex: + data.append(ref_ex.group('prefix')) + data.append(Ref(ref_ex.group('ref_name'))) + + if ref_ex.group('postfix'): + data.append(ref_ex.group('postfix')) + continue + + else: + data.append(line) except IOError: raise IOError('Error opening or reading file: {}'.format(filepath)) + print data return Base64(Join(delimiter, data)) From 6a9e3d99fe658abca9d465d39e65eb26e9673806 Mon Sep 17 00:00:00 2001 From: Jae Kun Choi Date: Mon, 22 May 2017 23:55:06 +1000 Subject: [PATCH 02/14] Removed unnecessary code and renamed var names --- troposphere/helpers/userdata.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py index 017b5ab74..d551243c5 100644 --- a/troposphere/helpers/userdata.py +++ b/troposphere/helpers/userdata.py @@ -24,24 +24,23 @@ def from_file(filepath, delimiter='', blanklines=False): :return The base64 representation of the file. """ - ref_pattern = re.compile('(?P.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])') + ref_pattern = re.compile('(?P
.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])')
     data = []
 
     try:
         with open(filepath, 'r') as f:
             for line in f:
-                print line
                 if blanklines and line.strip('\n\r ') == '':
                     continue
 
                 ref_ex = ref_pattern.match(line)
 
                 if ref_ex:
-                    data.append(ref_ex.group('prefix'))
-                    data.append(Ref(ref_ex.group('ref_name')))
+                    data.append(ref_ex.group('pre'))
+                    data.append(Ref(ref_ex.group('ref')))
 
-                    if ref_ex.group('postfix'):
-                        data.append(ref_ex.group('postfix'))
+                    if ref_ex.group('post'):
+                        data.append(ref_ex.group('post'))
                         continue
 
                 else:
@@ -49,5 +48,4 @@ def from_file(filepath, delimiter='', blanklines=False):
     except IOError:
         raise IOError('Error opening or reading file: {}'.format(filepath))
 
-    print data
     return Base64(Join(delimiter, data))

From 47c59806f9349154c382ca7f3986e93154da6f7e Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Mon, 22 May 2017 23:56:36 +1000
Subject: [PATCH 03/14] Changed to more meaningful var name

---
 troposphere/helpers/userdata.py | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py
index d551243c5..fbf51fe0b 100644
--- a/troposphere/helpers/userdata.py
+++ b/troposphere/helpers/userdata.py
@@ -24,7 +24,7 @@ def from_file(filepath, delimiter='', blanklines=False):
     :return The base64 representation of the file.
     """
 
-    ref_pattern = re.compile('(?P
.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])')
+    ref_pattern = re.compile('(?P.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])')
     data = []
 
     try:
@@ -36,11 +36,11 @@ def from_file(filepath, delimiter='', blanklines=False):
                 ref_ex = ref_pattern.match(line)
 
                 if ref_ex:
-                    data.append(ref_ex.group('pre'))
-                    data.append(Ref(ref_ex.group('ref')))
+                    data.append(ref_ex.group('prefix'))
+                    data.append(Ref(ref_ex.group('reference')))
 
-                    if ref_ex.group('post'):
-                        data.append(ref_ex.group('post'))
+                    if ref_ex.group('suffix'):
+                        data.append(ref_ex.group('suffix'))
                         continue
 
                 else:

From e572cef2f6205aa8f8cdd230967e158a54025e4c Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 00:07:00 +1000
Subject: [PATCH 04/14] Added unit test for passing in Ref function

---
 tests/test_userdata.py                  | 4 ++++
 tests/userdata_test_scripts/ref_func.sh | 1 +
 2 files changed, 5 insertions(+)
 create mode 100644 tests/userdata_test_scripts/ref_func.sh

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index 7ce9c8402..5c12b8f76 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -56,6 +56,10 @@ def test_char_escaping(self):
     def test_nonexistant_file(self):
         self.assertRaises(IOError, self.create_result, 'nonexistant.sh')
 
+    def test_if_ref_is_replaced(self):
+        result = self.create_result('ref_func.sh')
+        answer = self.create_answer(['/opt/aws/bin/cfn-init -v --stack "Ref(\'AWS::StackName\')" \\\n'])
+        self.assertEqual(result, answer)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/userdata_test_scripts/ref_func.sh b/tests/userdata_test_scripts/ref_func.sh
new file mode 100644
index 000000000..5b02d791a
--- /dev/null
+++ b/tests/userdata_test_scripts/ref_func.sh
@@ -0,0 +1 @@
+/opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')" \

From 17b6ad16ee61f0d6ff4744c159c671faa458dd35 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 00:44:14 +1000
Subject: [PATCH 05/14] Fix for PEP8

---
 tests/test_userdata.py          | 4 +++-
 troposphere/helpers/userdata.py | 5 ++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index 5c12b8f76..e2ff6d749 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -58,8 +58,10 @@ def test_nonexistant_file(self):
 
     def test_if_ref_is_replaced(self):
         result = self.create_result('ref_func.sh')
-        answer = self.create_answer(['/opt/aws/bin/cfn-init -v --stack "Ref(\'AWS::StackName\')" \\\n'])
+        answer = self.create_answer(["""/opt/aws/bin/cfn-init -v \
+--stack "Ref(\'AWS::StackName\')" \\\n"""])
         self.assertEqual(result, answer)
 
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py
index fbf51fe0b..3c3566294 100644
--- a/troposphere/helpers/userdata.py
+++ b/troposphere/helpers/userdata.py
@@ -24,7 +24,10 @@ def from_file(filepath, delimiter='', blanklines=False):
     :return The base64 representation of the file.
     """
 
-    ref_pattern = re.compile('(?P.*)Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)(?P.*[^\\*])')
+    pattern = r"""(?P.*)
+    Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)
+    (?P.*[^\\*])"""
+    ref_pattern = re.compile(pattern, re.VERBOSE)
     data = []
 
     try:

From 2f69e554055b9bb67c31ae4b399fcb83a93bd054 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 00:48:08 +1000
Subject: [PATCH 06/14] Adding imports for usage within userdata

---
 troposphere/helpers/userdata.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py
index 3c3566294..a0df2c156 100644
--- a/troposphere/helpers/userdata.py
+++ b/troposphere/helpers/userdata.py
@@ -1,6 +1,8 @@
 #!/usr/bin/python
 
-from troposphere import Base64, Join
+import re
+
+from troposphere import Base64, Join, Ref
 
 
 def from_file(filepath, delimiter='', blanklines=False):

From 0de37f91ed14fc1adb97258027e2311c149bfa26 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 00:54:09 +1000
Subject: [PATCH 07/14] Fixing for unit test failure on Python 2.7.9

---
 tests/test_userdata.py                  | 3 +--
 tests/userdata_test_scripts/ref_func.sh | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index e2ff6d749..d3c7e803d 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -58,8 +58,7 @@ def test_nonexistant_file(self):
 
     def test_if_ref_is_replaced(self):
         result = self.create_result('ref_func.sh')
-        answer = self.create_answer(["""/opt/aws/bin/cfn-init -v \
---stack "Ref(\'AWS::StackName\')" \\\n"""])
+        answer = self.create_answer(['--stack "Ref(\'AWS::StackName\')" \\\n'])
         self.assertEqual(result, answer)
 
 
diff --git a/tests/userdata_test_scripts/ref_func.sh b/tests/userdata_test_scripts/ref_func.sh
index 5b02d791a..d994ee0e2 100644
--- a/tests/userdata_test_scripts/ref_func.sh
+++ b/tests/userdata_test_scripts/ref_func.sh
@@ -1 +1 @@
-/opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')" \
+--stack "Ref('AWS::StackName')" \

From 2b266802bdbd1b19d702da15004596a4a9735fec Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 18:08:16 +1000
Subject: [PATCH 08/14] Fixed PEP8 issue with E501 line too long

---
 tests/test_userdata.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index d3c7e803d..5606d8af4 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -58,7 +58,11 @@ def test_nonexistant_file(self):
 
     def test_if_ref_is_replaced(self):
         result = self.create_result('ref_func.sh')
-        answer = self.create_answer(['--stack "Ref(\'AWS::StackName\')" \\\n'])
+        answer = self.create_answer([
+            '--stack "',
+            '{\'Ref\': \'AWS::StackName\'}',
+            '" \\\n'
+            ])
         self.assertEqual(result, answer)
 
 

From c9f8f4f47133b8dc256f6fbd9f32d64cb2c66de7 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 19:34:48 +1000
Subject: [PATCH 09/14] PEP8 fixes - E128 continuation line under-indented for
 visual indent

---
 tests/test_userdata.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index 5606d8af4..1d7849990 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -58,9 +58,12 @@ def test_nonexistant_file(self):
 
     def test_if_ref_is_replaced(self):
         result = self.create_result('ref_func.sh')
+
         answer = self.create_answer([
             '--stack "',
-            '{\'Ref\': \'AWS::StackName\'}',
+            {
+                'Ref': 'AWS::StackName'
+            },
             '" \\\n'
             ])
         self.assertEqual(result, answer)

From aeb88e72d19456253f4198f84d6b1817728697e1 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 19:49:28 +1000
Subject: [PATCH 10/14] Added README instructions of from_file helper usage

---
 README.rst | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/README.rst b/README.rst
index 5b6d6e957..8d4deabb9 100644
--- a/README.rst
+++ b/README.rst
@@ -255,6 +255,18 @@ Duplicating a single instance sample would look like this
 
     print(template.to_json())
 
+UserData
+========
+You can pass in user data script to your launch config using ```from_file``` helper function under helpers/userdata.py file.
+You can also use ```Ref``` method in the script file which will be translated into CloudFormation function.
+
+Example:
+
+    #!/bin/bash -ex
+
+    /opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')"
+
+
 Community
 =========
 

From d817a644c839fbb46ae3b3b57488ba0d04891c4b Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 20:52:14 +1000
Subject: [PATCH 11/14] README fixes for reStructuredText

---
 README.rst | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.rst b/README.rst
index 8d4deabb9..e4fcb105c 100644
--- a/README.rst
+++ b/README.rst
@@ -257,11 +257,13 @@ Duplicating a single instance sample would look like this
 
 UserData
 ========
-You can pass in user data script to your launch config using ```from_file``` helper function under helpers/userdata.py file.
+You can pass in user data script to your launch config using ``from_file`` helper function under helpers/userdata.py file.
 You can also use ```Ref``` method in the script file which will be translated into CloudFormation function.
 
 Example:
 
+.. code:: bash
+
     #!/bin/bash -ex
 
     /opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')"

From 771381f827ff0b37c3bbfd9dcf830137214d680f Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Tue, 23 May 2017 20:52:52 +1000
Subject: [PATCH 12/14] README fixes for reStructuredText

---
 README.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.rst b/README.rst
index e4fcb105c..446de83b7 100644
--- a/README.rst
+++ b/README.rst
@@ -258,7 +258,7 @@ Duplicating a single instance sample would look like this
 UserData
 ========
 You can pass in user data script to your launch config using ``from_file`` helper function under helpers/userdata.py file.
-You can also use ```Ref``` method in the script file which will be translated into CloudFormation function.
+You can also use ``Ref`` method in the script file which will be translated into CloudFormation function.
 
 Example:
 

From 0dd8596d6d2302e84fa9a7154592e1716cb1bd5d Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Sun, 25 Jun 2017 23:42:47 +1000
Subject: [PATCH 13/14] Added more robust userdata which is used to replace
 multiple refs in the test. Added condition to include ref search

---
 tests/test_userdata.py                  | 14 +++++++-----
 tests/userdata_test_scripts/ref_func.sh |  5 ++++-
 troposphere/helpers/userdata.py         | 29 +++++++++++++------------
 3 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/tests/test_userdata.py b/tests/test_userdata.py
index 1d7849990..23b4c61ca 100644
--- a/tests/test_userdata.py
+++ b/tests/test_userdata.py
@@ -60,12 +60,14 @@ def test_if_ref_is_replaced(self):
         result = self.create_result('ref_func.sh')
 
         answer = self.create_answer([
-            '--stack "',
-            {
-                'Ref': 'AWS::StackName'
-            },
-            '" \\\n'
-            ])
+            '/opt/aws/bin/cfn-init -v --stack "',
+            {'Ref': 'AWS::StackName'},
+            '" \\\n',
+            '  --resource LaunchConfig \\\n',
+            '  --configsets ConfigCluster \\\n',
+            '  --region ',
+            {'Ref': 'AWS::Region'},
+            '\n'])
         self.assertEqual(result, answer)
 
 
diff --git a/tests/userdata_test_scripts/ref_func.sh b/tests/userdata_test_scripts/ref_func.sh
index d994ee0e2..92f8ff644 100644
--- a/tests/userdata_test_scripts/ref_func.sh
+++ b/tests/userdata_test_scripts/ref_func.sh
@@ -1 +1,4 @@
---stack "Ref('AWS::StackName')" \
+/opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')" \
+  --resource LaunchConfig \
+  --configsets ConfigCluster \
+  --region Ref('AWS::Region')
diff --git a/troposphere/helpers/userdata.py b/troposphere/helpers/userdata.py
index a0df2c156..ab49a3e99 100644
--- a/troposphere/helpers/userdata.py
+++ b/troposphere/helpers/userdata.py
@@ -5,7 +5,7 @@
 from troposphere import Base64, Join, Ref
 
 
-def from_file(filepath, delimiter='', blanklines=False):
+def from_file(filepath, delimiter='', blanklines=False, searchref=True):
     """
     Imports userdata from a file.
     Also supports passing Troposphere Ref function
@@ -26,10 +26,6 @@ def from_file(filepath, delimiter='', blanklines=False):
     :return The base64 representation of the file.
     """
 
-    pattern = r"""(?P.*)
-    Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)
-    (?P.*[^\\*])"""
-    ref_pattern = re.compile(pattern, re.VERBOSE)
     data = []
 
     try:
@@ -38,18 +34,23 @@ def from_file(filepath, delimiter='', blanklines=False):
                 if blanklines and line.strip('\n\r ') == '':
                     continue
 
-                ref_ex = ref_pattern.match(line)
+                if searchref:
+                    pattern = r"""(?P.*)
+                    Ref\(\'(?P[a-zA-Z0-9\:]+)\'\)
+                    (?P.*[^\\*])"""
+                    ref_pattern = re.compile(pattern, re.VERBOSE)
 
-                if ref_ex:
-                    data.append(ref_ex.group('prefix'))
-                    data.append(Ref(ref_ex.group('reference')))
+                    ref_ex = ref_pattern.match(line)
 
-                    if ref_ex.group('suffix'):
-                        data.append(ref_ex.group('suffix'))
-                        continue
+                    if ref_ex:
+                        data.append(ref_ex.group('prefix'))
+                        data.append(Ref(ref_ex.group('reference')))
 
-                else:
-                    data.append(line)
+                        if ref_ex.group('suffix'):
+                            data.append(ref_ex.group('suffix'))
+                            continue
+
+                data.append(line)
     except IOError:
         raise IOError('Error opening or reading file: {}'.format(filepath))
 

From 15bddde89f76a17e3d16fc6150659e14fa768c84 Mon Sep 17 00:00:00 2001
From: Jae Kun Choi 
Date: Sun, 25 Jun 2017 23:47:05 +1000
Subject: [PATCH 14/14] Updated README to include multiple refs in userdata

---
 README.rst | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/README.rst b/README.rst
index 446de83b7..e93e07a92 100644
--- a/README.rst
+++ b/README.rst
@@ -266,7 +266,10 @@ Example:
 
     #!/bin/bash -ex
 
-    /opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')"
+    /opt/aws/bin/cfn-init -v --stack "Ref('AWS::StackName')" \
+      --resource LaunchConfig \
+      --configsets ConfigCluster \
+      --region Ref('AWS::Region')
 
 
 Community