Source code for kingpin.actors.aws.elbv2

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Copyright 2018 Nextdoor.com, Inc

"""
:mod:`kingpin.actors.aws.elbv2`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"""

import logging

from tornado import concurrent
from tornado import gen

from kingpin.actors import exceptions
from kingpin.actors.aws import base
from kingpin.actors.utils import dry
from kingpin.constants import REQUIRED

import botocore.exceptions

log = logging.getLogger(__name__)

__author__ = 'Matt Wise <matt@nextdoor.com>'


# This executor is used by the tornado.concurrent.run_on_executor()
# decorator. We would like this to be a class variable so its shared
# across RightScale objects, but we see testing IO errors when we
# do this.
EXECUTOR = concurrent.futures.ThreadPoolExecutor(10)


[docs]class RegisterInstance(base.AWSBaseActor): """Add an EC2 instance to a target group. **Options** :target_group: (str) Name of the Target Group ARN or its short name :instances: (str, list) Instance id, or list of ids. Default "self" id. :region: (str) AWS region (or zone) name, like us-west-2 **Example** .. code-block:: yaml --- actor: aws.elbv2.RegisterInstance desc: Run RegisterInstance options: target_group: prod-loadbalancer instances: i-123456 region: us-east-1 **Dry run** Will find the specified Target Group, but not take any actions regarding instances. """ all_options = { 'target_group': (str, REQUIRED, 'Name of the Target Group or its full ARN'), 'region': (str, REQUIRED, 'AWS region (or zone) name, like us-west-2'), 'instances': ((str, list), None, ( 'Instance id, or list of ids. If no value is specified then ' 'the instance id of the executing machine is used.')) } @gen.coroutine @dry('Would add {1} to {0}') def _add(self, arn, targets): """Registers the supplied Targets with the Target Group ARN. http://boto3.readthedocs.io/en/latest/reference/services/ elbv2.html#ElasticLoadBalancingv2.Client.register_targets Args: arn: ELBv2 Target ARN targets: A list of Instance IDs or Target IP Addresses. """ # TODO: In the future, add support for the optional Port and # AvailabilityZone parameters. For now, keeping this dead simple. targets = [{'Id': t} for t in targets] try: yield self.api_call( self.elbv2_conn.register_targets, TargetGroupArn=arn, Targets=targets) except botocore.exceptions.ClientError as e: raise exceptions.UnrecoverableActorFailure(e.message) @gen.coroutine def _execute(self): arn = yield self._find_target_group(self.option('target_group')) instances = self.option('instances') if not instances: self.log.debug('No instance provided. Using current instance id.') iid = yield self._get_meta_data('instance-id') instances = [iid] self.log.debug('Instances is: %s' % instances) if type(instances) is not list: instances = [instances] self.log.info(('Adding the following instances to Target Group: ' '%s' % ', '.join(instances))) yield self._add(arn, instances)
[docs]class DeregisterInstance(base.AWSBaseActor): """Remove EC2 instance(s) from a Target Group. **Options** :elb: (str) Name of the Target Group. :instances: (str, list) Instance id, or list of ids :region: (str) AWS region (or zone) name, like us-west-2 **Example** .. code-block:: yaml actor: aws.elbv2.DeregisterInstance desc: Run DeregisterInstance options: target_group: my-webserver-elb instances: i-abcdeft region: us-west-2 **Dry run** Will find the Target Group but not take any actions regarding the instances. """ all_options = { 'target_group': (str, REQUIRED, 'Name of the Target Group or its full ARN'), 'region': (str, REQUIRED, 'AWS region (or zone) name, like us-west-2'), 'instances': ((str, list), None, ( 'Instance id, or list of ids. If no value is specified then ' 'the instance id of the executing machine is used.')), } @gen.coroutine @dry('Would remove instances from {0}: {1}') def _remove(self, arn, targets): """Deregisters the supplied Targets with the Target Group ARN. http://boto3.readthedocs.io/en/latest/reference/services/ elbv2.html#ElasticLoadBalancingv2.Client.deregister_targets Args: arn: ELBv2 Target ARN targets: A list of Instance IDs or Target IP Addresses. """ # TODO: In the future, add support for the optional Port and # AvailabilityZone parameters. For now, keeping this dead simple. targets = [{'Id': t} for t in targets] try: yield self.api_call( self.elbv2_conn.deregister_targets, TargetGroupArn=arn, Targets=targets) except botocore.exceptions.ClientError as e: raise exceptions.UnrecoverableActorFailure(e.message) @gen.coroutine def _execute(self): arn = yield self._find_target_group(self.option('target_group')) instances = self.option('instances') if not instances: self.log.debug('No instance provided. Using current instance id.') iid = yield self._get_meta_data('instance-id') instances = [iid] self.log.debug('Instances is: %s' % instances) if type(instances) is not list: instances = [instances] self.log.info( ('Removing the following instances from the target group: ' '%s' % ', '.join(instances))) yield self._remove(arn, instances)