def test_broken_image(
        aws_profile,
        cloudtrails_to_delete,
        image_fixture,
):
    """Ensure Houndigrade handles broken image inspection gracefully.

    :id: 723A10AB-A729-41DE-93B2-966DCC6AD71D
    :description: When an instance is created from an image that is flawed
        (ie- garbled files -> yum.conf, /var/lib/rpm), it still gets
        inspected.
    :steps: 1) Create a user and authenticate with their password
        2) Create instances based off of a broken image
        3) Send a POST with the cloud account information to 'api/v1/account/'
        4) Send a GET to 'api/v1/instance/' and expect to get the instances we
            created
        5) Send a GET to 'api/v1/image/' and expect to get the image that
            the instances were based off of.
        6) Keep checking to see that the images progress from "pending",
            "preparing", "inspecting", to "inspected"
    :expectedresults:
        1) The server returns a 201 response with the information
            of the created account.
        2) We get 200 responses for our GET requests and information about
            the images includes inspection state information.
        3) The images are eventually inspected.
    """
    image_type, image_name, expected_state = broken_image
    image_fixture = image_fixture[2]
    source_image = image_fixture.source_image
    source_image_id = source_image['image_id']
    instance_id = image_fixture.instance_id

    auth = get_auth()
    client = api.Client(authenticate=False, response_handler=api.json_handler)
    aws_profile_name = aws_profile['name']
    aws_utils.delete_cloudtrail(
        (aws_profile_name, aws_profile['cloudtrail_name']))
    aws_utils.clean_cloudigrade_queues()

    # Create cloud account on cloudigrade
    cloud_account = {
        'name': aws_profile['name'],
        'account_arn': aws_profile['arn'],
        'resourcetype': AWS_ACCOUNT_TYPE
    }
    client.post(
        urls.CLOUD_ACCOUNT,
        payload=cloud_account,
        auth=auth
    )

    # Cleanup cloudtrail after test so events
    # don't keep coming into the cloudigrade s3 bucket
    cloudtrails_to_delete.append(
        (aws_profile['name'], aws_profile['cloudtrail_name'])
    )

    # Look for instances that should have been discovered
    # upon account creation.
    found_instances = wait_for_cloudigrade_instance(instance_id, auth)
    assert instance_id in found_instances

    # Look for images that should have been discovered
    # upon account creation.
    list_images = client.get(urls.IMAGE, auth=auth)
    found_images = [image['ec2_ami_id'] for image in list_images['results']]
    assert source_image_id in found_images
    wait_for_inspection(source_image, expected_state, auth)
def test_find_running_instances(
        test_case,
        aws_profile,
        cloudtrails_to_delete,
        image_fixture,
):
    """Ensure instances are discovered on account creation.

    :id: 741a0fad-45a8-4aab-9daa-11adad458d34
    :description: Ensure running instances are discovered and the images
        inspected.
    :steps: 1) Create a user and authenticate with their password
        2) Create instances based off of a non-windows image
        3) Send a POST with the cloud account information to 'api/v1/account/'
        4) Send a GET to 'api/v1/instance/' and expect to get the instances we
            created
        5) Send a GET to 'api/v1/image/' and expect to get the image that
            the instances were based off of.
        6) Keep checking to see that the images progress from "pending",
            "preparing", "inspecting", to "inspected"
    :expectedresults:
        1) The server returns a 201 response with the information
            of the created account.
        2) We get 200 responses for our GET requests and information about
            the images includes inspection state information.
        3) The images are eventually inspected.
    """
    image_type, image_name, expected_state = test_case
    bypass_inspection = False
    if image_fixture[0].image_name == test_case[1]:
        image_fixture = image_fixture[0]
        bypass_inspection = True
    else:
        image_fixture = image_fixture[1]

    source_image = image_fixture.source_image
    source_image_id = source_image['image_id']
    instance_id = image_fixture.instance_id
    if image_name != image_fixture.image_name \
            or image_type != image_fixture.image_type:
        pytest.skip(f'Only testing {IMAGES_TO_TEST}')

    auth = get_auth()
    client = api.Client(authenticate=False, response_handler=api.json_handler)
    aws_profile_name = aws_profile['name']
    aws_utils.delete_cloudtrail(
        (aws_profile_name, aws_profile['cloudtrail_name']))
    aws_utils.clean_cloudigrade_queues()

    # Create cloud account on cloudigrade
    cloud_account = {
        'name': aws_profile['name'],
        'account_arn': aws_profile['arn'],
        'resourcetype': AWS_ACCOUNT_TYPE
    }
    client.post(
        urls.CLOUD_ACCOUNT,
        payload=cloud_account,
        auth=auth
    )

    # Cleanup cloudtrail after test so events
    # don't keep coming into the cloudigrade s3 bucket
    cloudtrails_to_delete.append(
        (aws_profile['name'], aws_profile['cloudtrail_name'])
    )

    # Look for instances that should have been discovered
    # upon account creation.
    found_instances = wait_for_cloudigrade_instance(instance_id, auth)
    assert instance_id in found_instances

    # Look for images that should have been discovered
    # upon account creation.
    list_images = client.get(urls.IMAGE, auth=auth)
    found_images = [image['ec2_ami_id'] for image in list_images['results']]
    assert source_image_id in found_images
    if bypass_inspection:
        wait_for_inspection(source_image, expected_state, auth, timeout=200)
    else:
        wait_for_inspection(source_image, expected_state, auth)
def test_on_off_events(
    aws_profile,
    cloudtrails_to_delete,
    power_on_event,
    power_off_event,
    bad_event,
    image_fixture,
):
    """Ensure power on and power off events continue to be discovered.

    :id: 5c57b04a-dbe7-4ed7-ac6d-644bfa73d94f
    :description: Ensure power on and power off events are recorded when
        instances are powered on and either stopped or terminated.
    :steps: 1) Create a user and authenticate with their password
        2) Create an instance on AWS and stop it, to ensure we have a valid
            instance to reference.
        2) Send a POST with the cloud account information to 'api/v1/account/'
        3) Mock a power on event in the cloudigrade s3 bucket
        4) Mock a bad event in the cloudigrade bucket to make sure it is
            resilient to bad data also in the bucket.
        5) Keep checking '/api/v1/event/' until a 'power_on' event is found.
        6) Mock a power off event in the cloudigrade s3 bucket
        7) Keep checking '/api/v1/event/' until a 'power_off' event is found.
    :expectedresults:
        1) The server returns a 201 response with the information
            of the created account.
        2) We get 200 responses for our GET requests to the `/api/v1/event`
            endpoint.
        3) The power on events are eventually recorded.
        4) The bad events are ignored.
        5) The power on events events eventually recorded.
    """
    # check and make sure the instance is not running
    client = aws_utils.aws_session(aws_profile['name']).client('ec2')
    reservations = client.describe_instances(Filters=[{
        'Name': 'instance-state-name',
        'Values': [
            'running',
        ]}]).get('Reservations', [])
    running_instances = []
    for reservation in reservations:
        running_instances.extend([inst['InstanceId']
                                  for inst in reservation.get(
                                      'Instances', [])])
    # if it is running, go ahead and stop it so it does not provide
    # a "power_on" event on cloud account registration and circumvent
    # what we are trying to test.
    image_fixture = image_fixture[1]
    if image_fixture.instance_id in running_instances:
        client.stop_instances(InstanceIds=[image_fixture.instance_id])

    auth = get_auth()
    client = api.Client(authenticate=False, response_handler=api.json_handler)
    aws_profile_name = aws_profile['name']
    # Make sure we are working with a clean slate and cloudigrade
    # does not get data from previous registration.
    aws_utils.delete_cloudtrail(
        (aws_profile_name, aws_profile['cloudtrail_name']))
    aws_utils.clean_cloudigrade_queues()
    instance_id = image_fixture.instance_id
    bad_event_instance_id = image_fixture.instance_id
    bad_event_aws_profile = deepcopy(aws_profile)
    # Create cloud account on cloudigrade
    cloud_account = {
        'name': aws_profile['name'],
        'account_arn': aws_profile['arn'],
        'resourcetype': AWS_ACCOUNT_TYPE
    }
    client.post(
        urls.CLOUD_ACCOUNT,
        payload=cloud_account,
        auth=auth
    )

    # Cleanup cloudtrail after test so events
    # don't keep coming into the cloudigrade s3 bucket
    cloudtrails_to_delete.append(
        (aws_profile['name'], aws_profile['cloudtrail_name'])
    )

    power_on_event = create_event(
        instance_id,
        aws_profile,
        event_type=power_on_event)
    if bad_event.name == 'badawsaccount':
        bad_event_aws_profile['account_number'] = 123
    elif bad_event.name == 'badinstanceid':
        bad_event_instance_id = 'i-123'
    for _ in range(random.randint(1, 10)):
        create_event(
            bad_event_instance_id,
            bad_event_aws_profile,
            event_type='BadEvent',
            data=bad_event.data,
            gzipped=bad_event.gzipped
        )

    wait_for_instance_event(
        instance_id,
        'power_on',
        auth,
        aws_profile_name,
        power_on_event
    )

    power_off_event = create_event(
        instance_id,
        aws_profile,
        event_type=power_off_event)
    wait_for_instance_event(
        instance_id,
        'power_off',
        auth,
        aws_profile_name,
        power_off_event
    )
Ejemplo n.º 4
0
def customer_aws_reaper(env_cloudtrail_only=False,
                        all_integrade_cloudtrails=False):
    """Clean up customer accounts from all testing activities.

    Terminate all instances and delete dangling volumes. De-registers the AMIs
    created by cloudigrade as copies of shared private AMIs and the associated
    snapshots. Also deletes the cloudtrail associated with the current
    DEPLOYMENT_PREFIX. Optionally can delete all cloudtrails created by
    integrade environments. This option is relativly heavy handed, as it
    deletes any cloudtrail that has 'integrade' in the name.

    Iterates over all customers profiles configured for integrade to use as
    customer accounts and terminates their instances and deletes any dangling
    EBS volumes. Useful for periodic clean up but DANGEROUS! DELETES stuff!!!
    Do not use if you think you have important things running in these
    accounts.

    Expects all the same configuration as used by the tests to be present in
    the environment, most critcally the AWS credentials for any customer
    accounts.

    Command line arguments::

        --env-cloudtrail-only
        --all-integrade-cloudtrails

    The ``--env-cloudtrail-only`` option makes it so that **only** the
    cloudtrail associated with this environments DEPLOYMENT_PREFIX is deleted,
    and **no other action is taken with any instances or AMIs**. This option
    takes precedence over any other option.

    The ``--all-integrade-cloudtrails`` option deletes **all** cloudtrails with
    'integrade' in the name of the trail. When this option is passed all
    other cleanup activities are also taken, so all instances are terminated
    and cloudigrade AMI copies are deleted, etc.

    Example::

        # in a python 3 virutal environment
        $ make install-dev
        # would terminate all instances and also the cloudtrail for this env
        $ python scripts/aws_reaper.py

        # would terminate all instances and also all integrade cloudtrails
        # and also any cloudtrail in the customer accounts with
        # $CLOUDTRAIL_PREFIX
        $ python scripts/aws_reaper.py --all-integrade-cloudtrails

        # would ONLY delete the cloudtrail for this environment
        $ python scripts/aws_reaper.py --env-cloudtrail-only

        # Equivalent to previous example,
        # would ONLY delete the cloudtrail for this environment
        $ python scripts/aws_reaper.py --env-cloudtrail-only --all-integrade-cloudtrails # noqa E501


    This script is called by a nightly job running on gitlab-ci, and is meant
    to help reduce any detritus we leave behind on AWS during daily testing on
    the accounts used by automation as customers.
    """
    cfg = config.get_config(create_superuser=False, need_base_url=False)
    for profile in cfg['aws_profiles']:
        aws_utils.delete_cloudtrail(
            (profile['name'], profile['cloudtrail_name']))
        if env_cloudtrail_only:
            continue
        else:
            aws_utils.terminate_all_instances(profile['name'])
            aws_utils.delete_available_volumes(profile['name'])
            aws_utils.clean_up_cloudigrade_ami_copies(profile['name'])
            if all_integrade_cloudtrails:
                session = aws_utils.aws_session(profile['name'])
                client = session.client('cloudtrail')
                trail_names = [
                    trail['Name']
                    for trail in client.describe_trails()['trailList']
                ]
                for trail_name in trail_names:
                    if 'integrade' in trail_name:
                        client.delete_trail(Name=trail_name)