Repository: zalando-stups/senza

Issue:
Better error message for "create" and VPC tags
When trying to create a stack with a VPC that has no tags the user gets the following message:

```
senza create deploy-definition.yaml 1 0.1
Generating Cloud Formation template.. EXCEPTION OCCURRED: 'NoneType' object is not iterable
Unknown Error: 'NoneType' object is not iterable.
Please create an issue with the content of /var/folders/yd/p61l98fn2g9fffwgjs819gr1sprr6d/T/senza-traceback-xgrqlxbj
```

In /var/folders/yd/p61l98fn2g9fffwgjs819gr1sprr6d/T/senza-traceback-xgrqlxbj:

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/site-packages/senza/error_handling.py", line 76, in __call__
    self.function(*args, **kwargs)
  File "/usr/local/lib/python3.5/site-packages/click/core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.5/site-packages/click/core.py", line 696, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.5/site-packages/click/core.py", line 1060, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.5/site-packages/click/core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.5/site-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.5/site-packages/senza/cli.py", line 663, in create
    data = create_cf_template(definition, region, version, parameter, force, parameter_file)
  File "/usr/local/lib/python3.5/site-packages/senza/cli.py", line 746, in create_cf_template
    data = evaluate(definition.copy(), args, account_info, force)
  File "/usr/local/lib/python3.5/site-packages/senza/cli.py", line 242, in evaluate
    definition = componentfn(definition, configuration, args, info, force, account_info)
  File "/usr/local/lib/python3.5/site-packages/senza/components/stups_auto_configuration.py", line 31, in component_stups_auto_configuration
    vpc_id = configuration.get('VpcId', account_info.VpcID)
  File "/usr/local/lib/python3.5/site-packages/senza/cli.py", line 329, in VpcID
    vpc = ec2.get_default_vpc()
  File "/usr/local/lib/python3.5/site-packages/senza/manaus/ec2.py", line 71, in get_default_vpc
    return EC2VPC.from_boto_vpc(vpc)
  File "/usr/local/lib/python3.5/site-packages/senza/manaus/ec2.py", line 41, in from_boto_vpc
    return cls(vpc.vpc_id, vpc.is_default, vpc.tags)
  File "/usr/local/lib/python3.5/site-packages/senza/manaus/ec2.py", line 22, in __init__
    self.tags = OrderedDict([(t['Key'], t['Value']) for t in tags])  # type: Dict[str, str]
TypeError: 'NoneType' object is not iterable
```

The error message should be more descriptive.

Discussion:

Patch:
diff --git a/senza/manaus/ec2.py b/senza/manaus/ec2.py
index 9ea2600..6dee960 100644
--- a/senza/manaus/ec2.py
+++ b/senza/manaus/ec2.py
@@ -1,5 +1,5 @@
 from collections import OrderedDict
-from typing import Dict, List, Iterator
+from typing import Dict, List, Iterator, Optional
 
 import boto3
 
@@ -16,9 +16,10 @@ class EC2VPC:
     def __init__(self,
                  vpc_id: str,
                  is_default: bool,
-                 tags: List[Dict[str, str]]):
+                 tags: Optional[List[Dict[str, str]]]):
         self.vpc_id = vpc_id
         self.is_default = is_default
+        tags = tags or []  # type: List[Dict[str, str]]
         self.tags = OrderedDict([(t['Key'], t['Value']) for t in tags])  # type: Dict[str, str]
 
         self.name = self.tags.get('Name', self.vpc_id)


Test patch:
diff --git a/tests/test_manaus/test_ec2.py b/tests/test_manaus/test_ec2.py
index 36f1588..4dd7ae6 100644
--- a/tests/test_manaus/test_ec2.py
+++ b/tests/test_manaus/test_ec2.py
@@ -37,6 +37,11 @@ def test_get_default_vpc(monkeypatch):
     mock_vpc3.is_default = False
     mock_vpc3.tags = []
 
+    mock_vpc4 = MagicMock()
+    mock_vpc4.vpc_id = 'vpc-id4'
+    mock_vpc4.is_default = True
+    mock_vpc4.tags = None
+
     m_resource = MagicMock()
     m_resource.return_value = m_resource
     monkeypatch.setattr('boto3.resource', m_resource)
@@ -59,11 +64,16 @@ def test_get_default_vpc(monkeypatch):
         ec2.get_default_vpc()
     assert str(exc_info.value) == "Can't find any VPC!"
 
-    # no vpcs
+    # multiple vpcs
     m_resource.vpcs.all.return_value = [mock_vpc2, mock_vpc3]
     with pytest.raises(VPCError) as exc_info:
         ec2.get_default_vpc()
 
+    # no tags in vpc return default vpc
+    m_resource.vpcs.all.return_value = [mock_vpc4, mock_vpc2]
+    vpc3 = ec2.get_default_vpc()
+    assert vpc3.vpc_id == 'vpc-id4'
+
     assert str(exc_info.value) == ("Multiple VPCs are only supported if one "
                                    "VPC is the default VPC (IsDefault=true)!")