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
    )
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)