diff --git a/hacking/aws_config/testing_policies/compute-policy.json b/hacking/aws_config/testing_policies/compute-policy.json index 26932dfa5e9..1d065d33afb 100644 --- a/hacking/aws_config/testing_policies/compute-policy.json +++ b/hacking/aws_config/testing_policies/compute-policy.json @@ -245,6 +245,23 @@ "Resource": [ "arn:aws:states:*" ] + }, + { + "Sid": "AllowLightsail", + "Effect": "Allow", + "Action": [ + "lightsail:CreateInstances", + "lightsail:CreateKeyPair", + "lightsail:DeleteInstance", + "lightsail:DeleteKeyPair", + "lightsail:GetInstance", + "lightsail:GetInstances", + "lightsail:GetKeyPairs", + "lightsail:RebootInstance", + "lightsail:StartInstance", + "lightsail:StopInstance" + ], + "Resource": "arn:aws:lightsail:*:*:*" } ] } diff --git a/lib/ansible/config/module_defaults.yml b/lib/ansible/config/module_defaults.yml index 717ea1ab425..c00841701d8 100644 --- a/lib/ansible/config/module_defaults.yml +++ b/lib/ansible/config/module_defaults.yml @@ -489,6 +489,8 @@ groupings: - aws lightsail: - aws + lightsail_keypair: + - aws rds: - aws rds_instance: diff --git a/test/integration/targets/lightsail/aliases b/test/integration/targets/lightsail/aliases new file mode 100644 index 00000000000..6e3860bee23 --- /dev/null +++ b/test/integration/targets/lightsail/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/test/integration/targets/lightsail/defaults/main.yml b/test/integration/targets/lightsail/defaults/main.yml new file mode 100644 index 00000000000..dc02cbc7ee5 --- /dev/null +++ b/test/integration/targets/lightsail/defaults/main.yml @@ -0,0 +1,3 @@ +instance_name: "{{ resource_prefix }}_instance" +keypair_name: "{{ resource_prefix }}_keypair" +zone: "{{ aws_region }}a" diff --git a/test/integration/targets/lightsail/library/lightsail_keypair.py b/test/integration/targets/lightsail/library/lightsail_keypair.py new file mode 100644 index 00000000000..ea27929fddf --- /dev/null +++ b/test/integration/targets/lightsail/library/lightsail_keypair.py @@ -0,0 +1,79 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +try: + from botocore.exceptions import ClientError, BotoCoreError + import boto3 +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils.aws.core import AnsibleAWSModule +from ansible.module_utils.ec2 import (get_aws_connection_info, boto3_conn) + + +def create_keypair(module, client, keypair_name): + """ + Create a keypair to use for your lightsail instance + """ + + try: + client.create_key_pair(keyPairName=keypair_name) + except ClientError as e: + if "Some names are already in use" in e.response['Error']['Message']: + module.exit_json(changed=False) + module.fail_json_aws(e) + + module.exit_json(changed=True) + + +def delete_keypair(module, client, keypair_name): + """ + Delete a keypair in lightsail + """ + + try: + client.delete_key_pair(keyPairName=keypair_name) + except ClientError as e: + if e.response['Error']['Code'] == "NotFoundException": + module.exit_json(changed=False) + module.fail_json_aws(e) + + module.exit_json(changed=True) + + +def main(): + + argument_spec = dict( + name=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + + module = AnsibleAWSModule(argument_spec=argument_spec) + region, ec2_url, aws_connect_params = get_aws_connection_info(module, boto3=True) + try: + client = boto3_conn(module, conn_type='client', resource='lightsail', region=region, endpoint=ec2_url, + **aws_connect_params) + except ClientError as e: + module.fail_json_aws(e) + + keypair_name = module.params.get('name') + state = module.params.get('state') + + if state == 'present': + create_keypair(module, client, keypair_name) + else: + delete_keypair(module, client, keypair_name) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/lightsail/tasks/main.yml b/test/integration/targets/lightsail/tasks/main.yml new file mode 100644 index 00000000000..d04497c1b37 --- /dev/null +++ b/test/integration/targets/lightsail/tasks/main.yml @@ -0,0 +1,132 @@ +--- + +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + + # ==== Tests =================================================== + + - name: Create a new keypair in lightsail + lightsail_keypair: + name: "{{ keypair_name }}" + + - name: Create a new instance + lightsail: + name: "{{ instance_name }}" + zone: "{{ zone }}" + blueprint_id: amazon_linux + bundle_id: nano_2_0 + key_pair_name: "{{ keypair_name }}" + register: result + + - assert: + that: + - result.changed == True + - "'instance' in result and result.instance.name == instance_name" + - "result.instance.state.name in ['pending', 'running']" + + - name: Make sure create is idempotent + lightsail: + name: "{{ instance_name }}" + zone: "{{ zone }}" + blueprint_id: amazon_linux + bundle_id: nano_2_0 + key_pair_name: "{{ keypair_name }}" + register: result + + - assert: + that: + - result.changed == False + + - name: Start the running instance + lightsail: + name: "{{ instance_name }}" + state: running + register: result + + - assert: + that: + - result.changed == False + + - name: Stop the instance + lightsail: + name: "{{ instance_name }}" + state: stopped + register: result + + - assert: + that: + - result.changed == True + - "result.instance.state.name in ['stopping', 'stopped']" + + - name: Stop the stopped instance + lightsail: + name: "{{ instance_name }}" + state: stopped + register: result + + - assert: + that: + - result.changed == False + + - name: Start the instance + lightsail: + name: "{{ instance_name }}" + state: running + register: result + + - assert: + that: + - result.changed == True + - "result.instance.state.name in ['running', 'pending']" + + - name: Restart the instance + lightsail: + name: "{{ instance_name }}" + state: restarted + register: result + + - assert: + that: + - result.changed == True + + - name: Delete the instance + lightsail: + name: "{{ instance_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == True + + - name: Make sure instance deletion is idempotent + lightsail: + name: "{{ instance_name }}" + state: absent + register: result + + - assert: + that: + - result.changed == False + + # ==== Cleanup ==================================================== + + always: + + - name: Cleanup - delete instance + lightsail: + name: "{{ instance_name }}" + state: absent + ignore_errors: yes + + - name: Cleanup - delete keypair + lightsail_keypair: + name: "{{ keypair_name }}" + state: absent + ignore_errors: yes