forked from ansible-collections/amazon.aws
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New module - autoscaling_instance(_info) (ansible-collections#2296)
Depends-On: ansible/ansible-zuul-jobs#1885 SUMMARY Looking to simplify autoscaling_group, add autoscaling_instance as a new module so we can deprecate support for it in autoscaling_group ISSUE TYPE New Module Pull Request COMPONENT NAME autoscaling_instance ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis Reviewed-by: Mike Graves <[email protected]> Reviewed-by: Mark Chappell
- Loading branch information
Showing
29 changed files
with
3,476 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright: Contributors to the Ansible project | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
# try: | ||
# import botocore | ||
# except ImportError: | ||
# pass # Modules are responsible for handling this. | ||
|
||
from ..botocore import is_boto3_error_code | ||
from ..errors import AWSErrorHandler | ||
from ..exceptions import AnsibleAWSError | ||
|
||
|
||
class AnsibleAutoScalingError(AnsibleAWSError): | ||
pass | ||
|
||
|
||
class AutoScalingErrorHandler(AWSErrorHandler): | ||
_CUSTOM_EXCEPTION = AnsibleAutoScalingError | ||
|
||
@classmethod | ||
def _is_missing(cls): | ||
return is_boto3_error_code("NoSuchEntity") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright: Contributors to the Ansible project | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from ..retries import AWSRetry | ||
|
||
# from .common import AnsibleAutoScalingError | ||
from .common import AutoScalingErrorHandler | ||
|
||
|
||
@AutoScalingErrorHandler.list_error_handler("list auto scaling groups", default_value=[]) | ||
@AWSRetry.jittered_backoff() | ||
def describe_auto_scaling_groups(client, group_names=None, filters=None): | ||
args = {} | ||
if group_names: | ||
args["AutoScalingGroupNames"] = group_names | ||
if filters: | ||
args["Filters"] = filters | ||
|
||
paginator = client.get_paginator("describe_auto_scaling_groups") | ||
return paginator.paginate(**args).build_full_result()["AutoScalingGroups"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright: Contributors to the Ansible project | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from ..retries import AWSRetry | ||
|
||
# from .common import AnsibleAutoScalingError | ||
from .common import AutoScalingErrorHandler | ||
|
||
|
||
@AutoScalingErrorHandler.list_error_handler("list auto scaling instances", default_value=[]) | ||
@AWSRetry.jittered_backoff() | ||
def describe_auto_scaling_instances(client, instance_ids=None): | ||
args = {} | ||
if instance_ids: | ||
args["InstanceIds"] = instance_ids | ||
|
||
paginator = client.get_paginator("describe_auto_scaling_instances") | ||
return paginator.paginate(**args).build_full_result()["AutoScalingInstances"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright: Contributors to the Ansible project | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from __future__ import annotations | ||
|
||
import typing | ||
|
||
if typing.TYPE_CHECKING: | ||
from typing import Optional | ||
|
||
from ..transformation import AnsibleAWSResource | ||
from ..transformation import AnsibleAWSResourceList | ||
from ..transformation import BotoResource | ||
from ..transformation import BotoResourceList | ||
|
||
from ..transformation import boto3_resource_list_to_ansible_dict | ||
from ..transformation import boto3_resource_to_ansible_dict | ||
|
||
|
||
def _inject_asg_name( | ||
instance: BotoResource, | ||
group_name: Optional[str] = None, | ||
) -> BotoResource: | ||
if not group_name: | ||
return instance | ||
if "AutoScalingGroupName" in instance: | ||
return instance | ||
instance["AutoScalingGroupName"] = group_name | ||
return instance | ||
|
||
|
||
def normalize_autoscaling_instance( | ||
instance: BotoResource, | ||
group_name: Optional[str] = None, | ||
) -> AnsibleAWSResource: | ||
"""Converts an AutoScaling Instance from the CamelCase boto3 format to the snake_case Ansible format. | ||
Also handles inconsistencies in the output between describe_autoscaling_group() and describe_autoscaling_instances(). | ||
""" | ||
if not instance: | ||
return instance | ||
|
||
# describe_autoscaling_group doesn't add AutoScalingGroupName | ||
instance = _inject_asg_name(instance, group_name) | ||
|
||
try: | ||
# describe_autoscaling_group and describe_autoscaling_instances aren't consistent | ||
instance["HealthStatus"] = instance["HealthStatus"].upper() | ||
except KeyError: | ||
pass | ||
|
||
return boto3_resource_to_ansible_dict(instance, force_tags=False) | ||
|
||
|
||
def normalize_autoscaling_instances( | ||
autoscaling_instances: BotoResourceList, | ||
group_name: Optional[str] = None, | ||
) -> AnsibleAWSResourceList: | ||
"""Converts a list of AutoScaling Instances from the CamelCase boto3 format to the snake_case Ansible format""" | ||
if not autoscaling_instances: | ||
return autoscaling_instances | ||
autoscaling_instances = [normalize_autoscaling_instance(i, group_name) for i in autoscaling_instances] | ||
return sorted(autoscaling_instances, key=lambda d: d.get("instance_id", None)) | ||
|
||
|
||
def normalize_autoscaling_groups(autoscaling_groups: BotoResourceList) -> AnsibleAWSResourceList: | ||
"""Converts a list of AutoScaling Groups from the CamelCase boto3 format to the snake_case Ansible format""" | ||
autoscaling_groups = boto3_resource_list_to_ansible_dict(autoscaling_groups) | ||
return sorted(autoscaling_groups, key=lambda d: d.get("auto_scaling_group_name", None)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright: Contributors to the Ansible project | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from ..waiter import BaseWaiterFactory | ||
|
||
WAITER_MAP = { | ||
"Standby": "instances_in_standby", | ||
"Terminated": "instances_terminated", | ||
"Detached": "instances_detached", | ||
"InService": "instances_in_service", | ||
"HEALTHY": "instances_healthy", | ||
"Healthy": "instances_healthy", | ||
"UNHEALTHY": "instances_unhealthy", | ||
"Unhealthy": "instances_unhealthy", | ||
"Protected": "instances_protected", | ||
"NotProtected": "instances_not_protected", | ||
} | ||
|
||
|
||
def _fail_on_instance_lifecycle_states(state): | ||
return dict(state="failure", matcher="pathAny", expected=state, argument="AutoScalingInstances[].LifecycleState") | ||
|
||
|
||
def _success_on_instance_lifecycle_states(state): | ||
return dict(state="success", matcher="pathAll", expected=state, argument="AutoScalingInstances[].LifecycleState") | ||
|
||
|
||
def _success_on_instance_health(health): | ||
return dict(state="success", matcher="pathAll", expected=health, argument="AutoScalingInstances[].HealthStatus") | ||
|
||
|
||
def _success_on_instance_protection(state): | ||
return dict( | ||
state="success", matcher="pathAll", expected=state, argument="AutoScalingInstances[].ProtectedFromScaleIn" | ||
) | ||
|
||
|
||
def _no_instances(result): | ||
return dict(state=result, matcher="path", expected=True, argument="length(AutoScalingInstances[]) == `0`") | ||
|
||
|
||
class AutoscalingWaiterFactory(BaseWaiterFactory): | ||
@property | ||
def _waiter_model_data(self): | ||
data = dict( | ||
instances_healthy=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_health("HEALTHY"), | ||
# Terminated Instances can't reach "Healthy" | ||
_fail_on_instance_lifecycle_states("Terminating"), | ||
_fail_on_instance_lifecycle_states("Terminated"), | ||
_fail_on_instance_lifecycle_states("Terminating:Wait"), | ||
_fail_on_instance_lifecycle_states("Terminating:Proceed"), | ||
], | ||
), | ||
instances_unhealthy=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_health("UNHEALTHY"), | ||
# Instances in an unhealthy state can end up being automatically terminated | ||
_no_instances("success"), | ||
], | ||
), | ||
instances_protected=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_protection(True), | ||
], | ||
), | ||
instances_not_protected=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_protection(False), | ||
# Instances without protection can end up being automatically terminated | ||
_no_instances("success"), | ||
], | ||
), | ||
instances_in_service=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_lifecycle_states("InService"), | ||
# Terminated instances can't reach InService | ||
_fail_on_instance_lifecycle_states("Terminating"), | ||
_fail_on_instance_lifecycle_states("Terminated"), | ||
_fail_on_instance_lifecycle_states("Terminating:Wait"), | ||
_fail_on_instance_lifecycle_states("Terminating:Proceed"), | ||
], | ||
), | ||
instances_in_standby=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_lifecycle_states("Standby"), | ||
# Terminated instances can't reach Standby | ||
_fail_on_instance_lifecycle_states("Terminating"), | ||
_fail_on_instance_lifecycle_states("Terminated"), | ||
_fail_on_instance_lifecycle_states("Terminating:Wait"), | ||
_fail_on_instance_lifecycle_states("Terminating:Proceed"), | ||
], | ||
), | ||
instances_detached=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_lifecycle_states("Detached"), | ||
_no_instances("success"), | ||
], | ||
), | ||
instances_terminated=dict( | ||
operation="DescribeAutoScalingInstances", | ||
delay=5, | ||
maxAttempts=120, | ||
acceptors=[ | ||
_success_on_instance_lifecycle_states("Terminated"), | ||
_no_instances("success"), | ||
], | ||
), | ||
) | ||
|
||
return data | ||
|
||
|
||
waiter_factory = AutoscalingWaiterFactory() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.