mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-26 20:31:27 -07:00
* aws_eks_cluster: Improve output documentation This data is already returned by the module, it just wasn't documented. These fields are required for accessing the created Kubernetes API with e.g. the k8s_raw module. * aws_eks_cluster: Add wait functionality This enables further cluster configuration once it's created and active. 20 minutes was chosen as an arbitrary default, so that if it takes longer than the documented "usually less than 10 minutes" it's still likely to succeed. * Correct security group name in aws_eks tests * Improve teardown of aws_eks tests Fix minor teardown issues. The `pause` step is a placeholder until a waiter for `state: absent`
285 lines
9.1 KiB
Python
285 lines
9.1 KiB
Python
#!/usr/bin/python
|
|
# Copyright (c) 2017 Ansible Project
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
ANSIBLE_METADATA = {
|
|
'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'
|
|
}
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: aws_eks_cluster
|
|
short_description: Manage Elastic Kubernetes Service Clusters
|
|
description:
|
|
- Manage Elastic Kubernetes Service Clusters
|
|
version_added: "2.7"
|
|
|
|
author: Will Thames (@willthames)
|
|
|
|
options:
|
|
name:
|
|
description: Name of EKS cluster
|
|
required: True
|
|
version:
|
|
description: Kubernetes version - defaults to latest
|
|
role_arn:
|
|
description: ARN of IAM role used by the EKS cluster
|
|
subnets:
|
|
description: list of subnet IDs for the Kubernetes cluster
|
|
security_groups:
|
|
description: list of security group names or IDs
|
|
state:
|
|
description: desired state of the EKS cluster
|
|
choices:
|
|
- absent
|
|
- present
|
|
default: present
|
|
wait:
|
|
description: >-
|
|
Specifies whether the module waits until the cluster becomes active after
|
|
creation. It takes "usually less than 10 minutes" per AWS documentation.
|
|
type: bool
|
|
default: 'no'
|
|
wait_timeout:
|
|
description: >-
|
|
The duration in seconds to wait for the cluster to become active. Defaults
|
|
to 1200 seconds (20 minutes).
|
|
default: 1200
|
|
|
|
|
|
requirements: [ 'botocore', 'boto3' ]
|
|
extends_documentation_fragment:
|
|
- aws
|
|
- ec2
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
# Note: These examples do not set authentication details, see the AWS Guide for details.
|
|
|
|
- name: Create an EKS cluster
|
|
aws_eks_cluster:
|
|
name: my_cluster
|
|
version: v1.10.0
|
|
role_arn: my_eks_role
|
|
subnets:
|
|
- subnet-aaaa1111
|
|
security_groups:
|
|
- my_eks_sg
|
|
- sg-abcd1234
|
|
register: caller_facts
|
|
|
|
- name: Remove an EKS cluster
|
|
aws_eks_cluster:
|
|
name: my_cluster
|
|
state: absent
|
|
'''
|
|
|
|
RETURN = '''
|
|
arn:
|
|
description: ARN of the EKS cluster
|
|
returned: when state is present
|
|
type: string
|
|
sample: arn:aws:eks:us-west-2:111111111111:cluster/my-eks-cluster
|
|
certificate_authority:
|
|
description: Dictionary containing Certificate Authority Data for cluster
|
|
returned: after creation
|
|
type: complex
|
|
contains:
|
|
data:
|
|
description: Base-64 encoded Certificate Authority Data for cluster
|
|
returned: when the cluster has been created and is active
|
|
type: string
|
|
endpoint:
|
|
description: Kubernetes API server endpoint
|
|
returned: when the cluster has been created and is active
|
|
type: string
|
|
sample: https://API_SERVER_ENDPOINT.yl4.us-west-2.eks.amazonaws.com
|
|
created_at:
|
|
description: Cluster creation date and time
|
|
returned: when state is present
|
|
type: string
|
|
sample: '2018-06-06T11:56:56.242000+00:00'
|
|
name:
|
|
description: EKS cluster name
|
|
returned: when state is present
|
|
type: string
|
|
sample: my-eks-cluster
|
|
resources_vpc_config:
|
|
description: VPC configuration of the cluster
|
|
returned: when state is present
|
|
type: complex
|
|
contains:
|
|
security_group_ids:
|
|
description: List of security group IDs
|
|
returned: always
|
|
type: list
|
|
sample:
|
|
- sg-abcd1234
|
|
- sg-aaaa1111
|
|
subnet_ids:
|
|
description: List of subnet IDs
|
|
returned: always
|
|
type: list
|
|
sample:
|
|
- subnet-abcdef12
|
|
- subnet-345678ab
|
|
- subnet-cdef1234
|
|
vpc_id:
|
|
description: VPC id
|
|
returned: always
|
|
type: string
|
|
sample: vpc-a1b2c3d4
|
|
role_arn:
|
|
description: ARN of the IAM role used by the cluster
|
|
returned: when state is present
|
|
type: string
|
|
sample: arn:aws:iam::111111111111:role/aws_eks_cluster_role
|
|
status:
|
|
description: status of the EKS cluster
|
|
returned: when state is present
|
|
type: string
|
|
sample:
|
|
- CREATING
|
|
- ACTIVE
|
|
version:
|
|
description: Kubernetes version of the cluster
|
|
returned: when state is present
|
|
type: string
|
|
sample: '1.10'
|
|
'''
|
|
|
|
|
|
from ansible.module_utils.aws.core import AnsibleAWSModule, is_boto3_error_code
|
|
from ansible.module_utils.ec2 import camel_dict_to_snake_dict, get_ec2_security_group_ids_from_names
|
|
from ansible.module_utils.aws.waiters import get_waiter
|
|
|
|
try:
|
|
import botocore.exceptions
|
|
except ImportError:
|
|
pass # caught by AnsibleAWSModule
|
|
|
|
|
|
def ensure_present(client, module):
|
|
name = module.params.get('name')
|
|
subnets = module.params['subnets']
|
|
groups = module.params['security_groups']
|
|
wait = module.params.get('wait')
|
|
cluster = get_cluster(client, module)
|
|
try:
|
|
ec2 = module.client('ec2')
|
|
vpc_id = ec2.describe_subnets(SubnetIds=[subnets[0]])['Subnets'][0]['VpcId']
|
|
groups = get_ec2_security_group_ids_from_names(groups, ec2, vpc_id)
|
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
|
module.fail_json_aws(e, msg="Couldn't lookup security groups")
|
|
|
|
if cluster:
|
|
if set(cluster['resourcesVpcConfig']['subnetIds']) != set(subnets):
|
|
module.fail_json(msg="Cannot modify subnets of existing cluster")
|
|
if set(cluster['resourcesVpcConfig']['securityGroupIds']) != set(groups):
|
|
module.fail_json(msg="Cannot modify security groups of existing cluster")
|
|
if module.params.get('version') and module.params.get('version') != cluster['version']:
|
|
module.fail_json(msg="Cannot modify version of existing cluster")
|
|
|
|
if wait:
|
|
wait_until_cluster_active(client, module)
|
|
# Ensure that fields that are only available for active clusters are
|
|
# included in the returned value
|
|
cluster = get_cluster(client, module)
|
|
|
|
module.exit_json(changed=False, **camel_dict_to_snake_dict(cluster))
|
|
|
|
if module.check_mode:
|
|
module.exit_json(changed=True)
|
|
try:
|
|
params = dict(name=name,
|
|
roleArn=module.params['role_arn'],
|
|
resourcesVpcConfig=dict(
|
|
subnetIds=subnets,
|
|
securityGroupIds=groups),
|
|
clientRequestToken='ansible-create-%s' % name)
|
|
if module.params['version']:
|
|
params['version'] = module.params['version']
|
|
cluster = client.create_cluster(**params)['cluster']
|
|
except botocore.exceptions.EndpointConnectionError as e:
|
|
module.fail_json(msg="Region %s is not supported by EKS" % client.meta.region_name)
|
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
|
module.fail_json_aws(e, msg="Couldn't create cluster %s" % name)
|
|
|
|
if wait:
|
|
wait_until_cluster_active(client, module)
|
|
# Ensure that fields that are only available for active clusters are
|
|
# included in the returned value
|
|
cluster = get_cluster(client, module)
|
|
|
|
module.exit_json(changed=True, **camel_dict_to_snake_dict(cluster))
|
|
|
|
|
|
def ensure_absent(client, module):
|
|
name = module.params.get('name')
|
|
existing = get_cluster(client, module)
|
|
if not existing:
|
|
module.exit_json(changed=False)
|
|
if not module.check_mode:
|
|
try:
|
|
client.delete_cluster(name=module.params['name'])
|
|
except botocore.exceptions.EndpointConnectionError as e:
|
|
module.fail_json(msg="Region %s is not supported by EKS" % client.meta.region_name)
|
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
|
module.fail_json_aws(e, msg="Couldn't delete cluster %s" % name)
|
|
module.exit_json(changed=True)
|
|
|
|
|
|
def get_cluster(client, module):
|
|
name = module.params.get('name')
|
|
try:
|
|
return client.describe_cluster(name=name)['cluster']
|
|
except is_boto3_error_code('ResourceNotFoundException'):
|
|
return None
|
|
except botocore.exceptions.EndpointConnectionError as e: # pylint: disable=duplicate-except
|
|
module.fail_json(msg="Region %s is not supported by EKS" % client.meta.region_name)
|
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except
|
|
module.fail_json(e, msg="Couldn't get cluster %s" % name)
|
|
|
|
|
|
def wait_until_cluster_active(client, module):
|
|
name = module.params.get('name')
|
|
wait_timeout = module.params.get('wait_timeout')
|
|
|
|
waiter = get_waiter(client, 'cluster_active')
|
|
attempts = 1 + int(wait_timeout / waiter.config.delay)
|
|
waiter.wait(name=name, WaiterConfig={'MaxAttempts': attempts})
|
|
|
|
|
|
def main():
|
|
argument_spec = dict(
|
|
name=dict(required=True),
|
|
version=dict(),
|
|
role_arn=dict(),
|
|
subnets=dict(type='list'),
|
|
security_groups=dict(type='list'),
|
|
state=dict(choices=['absent', 'present'], default='present'),
|
|
wait=dict(default=False, type='bool'),
|
|
wait_timeout=dict(default=1200, type='int')
|
|
)
|
|
|
|
module = AnsibleAWSModule(
|
|
argument_spec=argument_spec,
|
|
required_if=[['state', 'present', ['role_arn', 'subnets', 'security_groups']]],
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
if not module.botocore_at_least("1.10.32"):
|
|
module.fail_json(msg="aws_eks_cluster module requires botocore >= 1.10.32")
|
|
|
|
client = module.client('eks')
|
|
|
|
if module.params.get('state') == 'present':
|
|
ensure_present(client, module)
|
|
else:
|
|
ensure_absent(client, module)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|