From 761a9ee00eb4b7e6c8e9e1305e46f153072c7518 Mon Sep 17 00:00:00 2001 From: Tobias Henkel Date: Wed, 23 Jan 2019 13:55:06 +0100 Subject: [PATCH] Support userdata for instances in aws In some cases we need to be able to launch instances with custom userdata also in aws. Change-Id: I0891961f16bb3bd728622d3413bd185978d79324 --- doc/source/configuration.rst | 8 ++++++++ nodepool/driver/aws/config.py | 9 +++++++-- nodepool/driver/aws/provider.py | 3 +++ nodepool/tests/fixtures/aws.yaml | 6 ++++++ nodepool/tests/unit/test_driver_aws.py | 17 ++++++++++++++++- .../notes/aws-userdata-0ed096fdce83a2b1.yaml | 5 +++++ 6 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/aws-userdata-0ed096fdce83a2b1.yaml diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index 6e27568ea..97c49d1ed 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -1812,6 +1812,14 @@ section of the configuration. If given, the size of the root EBS volume, in GiB. + .. attr:: userdata + :type: str + :default: None + + A string of userdata for a node. Example usage is to install + cloud-init package on image which will apply the userdata. + Additional info about options in cloud-config: + https://cloudinit.readthedocs.io/en/latest/topics/examples.html .. _`EBS volume type`: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html .. _`AWS region`: https://docs.aws.amazon.com/general/latest/gr/rande.html diff --git a/nodepool/driver/aws/config.py b/nodepool/driver/aws/config.py index c927d7e9c..25de6f26e 100644 --- a/nodepool/driver/aws/config.py +++ b/nodepool/driver/aws/config.py @@ -56,6 +56,7 @@ class ProviderLabel(ConfigValue): self.key_name = None self.volume_size = None self.volume_type = None + self.userdata = None # The ProviderPool object that owns this label. self.pool = None @@ -68,7 +69,9 @@ class ProviderLabel(ConfigValue): and other.instance_type == self.instance_type and other.key_name == self.key_name and other.volume_size == self.volume_size - and other.volume_type == self.volume_type) + and other.volume_type == self.volume_type + and other.userdata == self.userdata + ) return False def __repr__(self): @@ -121,6 +124,7 @@ class ProviderPool(ConfigPool): pl.key_name = label['key-name'] pl.volume_type = label.get('volume-type') pl.volume_size = label.get('volume-size') + pl.userdata = label.get('userdata', None) full_config.labels[label['name']].pools.append(self) def __eq__(self, other): @@ -223,7 +227,8 @@ class AwsProviderConfig(ProviderConfig): v.Required('instance-type'): str, v.Required('key-name'): str, 'volume-type': str, - 'volume-size': int + 'volume-size': int, + 'userdata': str, } pool = ConfigPool.getCommonSchemaDict() diff --git a/nodepool/driver/aws/provider.py b/nodepool/driver/aws/provider.py index b4a9dcd29..08e3a24a7 100644 --- a/nodepool/driver/aws/provider.py +++ b/nodepool/driver/aws/provider.py @@ -179,6 +179,9 @@ class AwsProvider(Provider): if label.pool.subnet_id: args['NetworkInterfaces'][0]['SubnetId'] = label.pool.subnet_id + if label.userdata: + args['UserData'] = label.userdata + # Default block device mapping parameters are embedded in AMIs. # We might need to supply our own mapping before lauching the instance. # We basically want to make sure DeleteOnTermination is true and be diff --git a/nodepool/tests/fixtures/aws.yaml b/nodepool/tests/fixtures/aws.yaml index e8a614996..52891a819 100644 --- a/nodepool/tests/fixtures/aws.yaml +++ b/nodepool/tests/fixtures/aws.yaml @@ -10,6 +10,7 @@ labels: - name: ubuntu1404-by-capitalized-filters - name: ubuntu1404-bad-config - name: ubuntu1404-non-host-key-checking + - name: ubuntu1404-userdata providers: - name: ec2-us-west-2 @@ -67,6 +68,11 @@ providers: cloud-image: ubuntu1404-bad-config instance-type: t3.medium key-name: zuul + - name: ubuntu1404-userdata + cloud-image: ubuntu1404 + instance-type: t3.medium + key-name: zuul + userdata: fake-user-data - name: non-host-key-checking max-servers: 1 subnet-id: null diff --git a/nodepool/tests/unit/test_driver_aws.py b/nodepool/tests/unit/test_driver_aws.py index f6eeadb0b..b32f7b39b 100644 --- a/nodepool/tests/unit/test_driver_aws.py +++ b/nodepool/tests/unit/test_driver_aws.py @@ -12,6 +12,7 @@ # implied. # See the License for the specific language governing permissions and # limitations under the License. +import base64 import fixtures import logging @@ -34,7 +35,8 @@ class TestDriverAws(tests.DBTestCase): @mock_ec2 def _test_ec2_machine(self, label, is_valid_config=True, - host_key_checking=True): + host_key_checking=True, + userdata=None): aws_id = 'AK000000000000000000' aws_key = '0123456789abcdef0123456789abcdef0123456789abcdef' self.useFixture( @@ -42,6 +44,7 @@ class TestDriverAws(tests.DBTestCase): self.useFixture( fixtures.EnvironmentVariable('AWS_SECRET_ACCESS_KEY', aws_key)) + ec2_resource = boto3.resource('ec2', region_name='us-west-2') ec2 = boto3.client('ec2', region_name='us-west-2') # TEST-NET-3 @@ -104,6 +107,14 @@ class TestDriverAws(tests.DBTestCase): port=22, timeout=180, gather_hostkeys=True) + if userdata: + instance = ec2_resource.Instance(node.external_id) + response = instance.describe_attribute( + Attribute='userData') + self.assertIn('UserData', response) + userdata = base64.b64decode( + response['UserData']['Value']).decode() + self.assertEqual('fake-user-data', userdata) # A new request will be paused and for lack of quota # until this one is deleted @@ -154,3 +165,7 @@ class TestDriverAws(tests.DBTestCase): def test_ec2_machine_non_host_key_checking(self): self._test_ec2_machine('ubuntu1404-non-host-key-checking', host_key_checking=False) + + def test_ec2_machine_userdata(self): + self._test_ec2_machine('ubuntu1404-userdata', + userdata=True) diff --git a/releasenotes/notes/aws-userdata-0ed096fdce83a2b1.yaml b/releasenotes/notes/aws-userdata-0ed096fdce83a2b1.yaml new file mode 100644 index 000000000..ae9af8765 --- /dev/null +++ b/releasenotes/notes/aws-userdata-0ed096fdce83a2b1.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The AWS driver now supports custom + :attr:`providers.[aws].pools.labels.userdata` when launching instances.