Repository: cloudtools/troposphere

Issue:
S3 Bucket constructor
**Expected result:** troposphere.s3.Bucket constructor accepts `template` as a second parameter.
**Actual result:** troposphere.s3.Bucket constructor does not accept `template` as a second parameter.

Example of code that should work:
```python
template = Template()
Bucket(
    'PlaylistsFailoverS3Bucket',
    template,
    AccessControl=Private,
    BucketName="test-bucket",
)
```


Discussion:
s3.Bucket() has a custom \_\_init\_\_() which does not support template or validation parameters. This is likely due to it being a historical implementation prior to a more uniform way to handle validation. This needs to be refactored and fixed.
What about such fix: https://github.com/cloudtools/troposphere/compare/master...Tonkonozhenko:master ?
The fix would be removing \_\_init\_\_(), moving the AccessControl validation logic into a validate() method, and validating (or adding) tests.

Resolving patch:
diff --git a/troposphere/s3.py b/troposphere/s3.py
--- a/troposphere/s3.py
+++ b/troposphere/s3.py
@@ -299,19 +299,10 @@ class Bucket(AWSObject):
         LogDeliveryWrite,
     ]
 
-    def __init__(self, name=None, **kwargs):
-
-        # note: 'name' is the resource title, not the bucket name
-
-        if not name and 'title' in kwargs:
-            name = kwargs.pop('title')
-        if not name:
-            raise TypeError("You must provide a title for the bucket resource")
-        super(Bucket, self).__init__(name, **kwargs)
-
-        if 'AccessControl' in kwargs and \
-                isinstance(kwargs['AccessControl'], basestring):
-            if kwargs['AccessControl'] not in self.access_control_types:
+    def validate(self):
+        access_control = self.properties.get('AccessControl')
+        if access_control is not None:
+            if access_control not in self.access_control_types:
                 raise ValueError('AccessControl must be one of "%s"' % (
                     ', '.join(self.access_control_types)))
 


Test patch:
diff --git a/tests/s3.py b/tests/s3.py
new file mode 100644
--- /dev/null
+++ b/tests/s3.py
@@ -0,0 +1,60 @@
+import unittest
+from troposphere import Template
+from troposphere.s3 import AccelerateConfiguration, Bucket, Private
+
+
+class TestBucketTemplate(unittest.TestCase):
+    def test_bucket_template(self):
+        title = 'PlaylistsFailoverS3Bucket'
+        template = Template()
+        Bucket(
+            title,
+            template,
+            AccessControl=Private,
+            BucketName="test-bucket",
+        )
+        self.assertIn(title, template.resources)
+
+
+class TestBucket(unittest.TestCase):
+    def test_bucket_accesscontrol(self):
+        Bucket('b', AccessControl='AuthenticatedRead').validate()
+
+    def test_bucket_accesscontrol_bad_string(self):
+        with self.assertRaises(ValueError):
+            Bucket('b', AccessControl='FooBar').validate()
+
+    def test_bucket_accesscontrol_bad_type(self):
+        with self.assertRaises(TypeError):
+            Bucket('b', AccessControl=123).validate()
+
+
+class TestS3AccelerateConfiguration(unittest.TestCase):
+    def test_accelerate_configuration_enabled(self):
+        ac = AccelerateConfiguration(
+            AccelerationStatus='Enabled',
+        )
+        self.assertEqual('Enabled', ac.AccelerationStatus)
+
+    def test_accelerate_configuration_suspended(self):
+        ac = AccelerateConfiguration(
+            AccelerationStatus='Suspended',
+        )
+        self.assertEqual('Suspended', ac.AccelerationStatus)
+
+    def test_accelerate_configuration_invalid_value(self):
+        with self.assertRaises(ValueError):
+            AccelerateConfiguration(AccelerationStatus='Invalid Value')
+
+    def test_s3_bucket_accelerate_configuration(self):
+        t = Template()
+        ac = AccelerateConfiguration(AccelerationStatus="Enabled")
+
+        b = Bucket("s3Bucket", AccelerateConfiguration=ac)
+        t.add_resource(b)
+        output = t.to_json()
+        self.assertIn('"AccelerationStatus": "Enabled"', output)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_basic.py b/tests/test_basic.py
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -3,7 +3,7 @@
 from troposphere import If, Join, Ref, Split, Sub, Template
 from troposphere import depends_on_helper
 from troposphere.ec2 import Instance, Route, SecurityGroupRule
-from troposphere.s3 import Bucket, AccelerateConfiguration
+from troposphere.s3 import Bucket
 from troposphere.elasticloadbalancing import HealthCheck
 from troposphere.validators import positive_integer
 
@@ -63,33 +63,6 @@ def test_resource_depends_on_attr(self):
         self.assertEqual(b1.title, b2.DependsOn)
 
 
-class TestS3AccelerateConfiguration(unittest.TestCase):
-    def test_accelerate_configuration_enabled(self):
-        ac = AccelerateConfiguration(
-            AccelerationStatus='Enabled',
-        )
-        self.assertEqual('Enabled', ac.AccelerationStatus)
-
-    def test_accelerate_configuration_suspended(self):
-        ac = AccelerateConfiguration(
-            AccelerationStatus='Suspended',
-        )
-        self.assertEqual('Suspended', ac.AccelerationStatus)
-
-    def test_accelerate_configuration_invalid_value(self):
-        with self.assertRaises(ValueError):
-            AccelerateConfiguration(AccelerationStatus='Invalid Value')
-
-    def test_s3_bucket_accelerate_configuration(self):
-        t = Template()
-        ac = AccelerateConfiguration(AccelerationStatus="Enabled")
-
-        b = Bucket("s3Bucket", AccelerateConfiguration=ac)
-        t.add_resource(b)
-        output = t.to_json()
-        self.assertIn('"AccelerationStatus": "Enabled"', output)
-
-
 def call_correct(x):
     return x