# Copyright (c) 2013 Amazon.com, Inc. or its affiliates.
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from __future__ import with_statement

import boto.utils

from datetime import datetime
from time import time
from tests.unit import AWSMockServiceTestCase

from boto.emr.connection import EmrConnection
from boto.emr.emrobject import BootstrapAction, BootstrapActionList, ClusterStatus, ClusterSummaryList, ClusterSummary, ClusterTimeline, InstanceInfo, InstanceList, InstanceGroupInfo, InstanceGroup, InstanceGroupList, JobFlow, JobFlowStepList, Step, StepSummaryList

# These tests are just checking the basic structure of
# the Elastic MapReduce code, by picking a few calls
# and verifying we get the expected results with mocked
# responses.  The integration tests actually verify the
# API calls interact with the service correctly.
class TestListClusters(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<ListClustersResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
  <ListClustersResult>
    <Clusters>
      <member>
        <Id>j-aaaaaaaaaaaa</Id>
        <Status>
          <StateChangeReason>
            <Message>Terminated by user request</Message>
            <Code>USER_REQUEST</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:26Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <Name>analytics test</Name>
      </member>
      <member>
        <Id>j-aaaaaaaaaaaab</Id>
        <Status>
          <StateChangeReason>
            <Message>Terminated by user request</Message>
            <Code>USER_REQUEST</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-21T02:53:08Z</CreationDateTime>
            <ReadyDateTime>2014-01-21T02:56:40Z</ReadyDateTime>
            <EndDateTime>2014-01-21T03:40:22Z</EndDateTime>
          </Timeline>
        </Status>
        <Name>test job</Name>
      </member>
    </Clusters>
  </ListClustersResult>
  <ResponseMetadata>
    <RequestId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</RequestId>
  </ResponseMetadata>
</ListClustersResponse>
        """

    def test_list_clusters(self):
        self.set_http_response(status_code=200)
        response = self.service_connection.list_clusters()

        self.assert_request_parameters({
            'Action': 'ListClusters',
            'Version': '2009-03-31',
        })

        self.assertTrue(isinstance(response, ClusterSummaryList))

        self.assertEqual(len(response.clusters), 2)
        self.assertTrue(isinstance(response.clusters[0], ClusterSummary))
        self.assertEqual(response.clusters[0].name, 'analytics test')

        self.assertTrue(isinstance(response.clusters[0].status, ClusterStatus))

        self.assertTrue(isinstance(response.clusters[0].status.timeline, ClusterTimeline))

        self.assertEqual(response.clusters[0].status.timeline.creationdatetime, '2014-01-24T01:21:21Z')
        self.assertEqual(response.clusters[0].status.timeline.readydatetime, '2014-01-24T01:25:26Z')
        self.assertEqual(response.clusters[0].status.timeline.enddatetime, '2014-01-24T02:19:46Z')


    def test_list_clusters_created_before(self):
        self.set_http_response(status_code=200)

        date = datetime.now()
        response = self.service_connection.list_clusters(created_before=date)

        self.assert_request_parameters({
            'Action': 'ListClusters',
            'CreatedBefore': date.strftime(boto.utils.ISO8601),
            'Version': '2009-03-31'
        })

    def test_list_clusters_created_after(self):
        self.set_http_response(status_code=200)

        date = datetime.now()
        response = self.service_connection.list_clusters(created_after=date)

        self.assert_request_parameters({
            'Action': 'ListClusters',
            'CreatedAfter': date.strftime(boto.utils.ISO8601),
            'Version': '2009-03-31'
        })

    def test_list_clusters_states(self):
        self.set_http_response(status_code=200)
        response = self.service_connection.list_clusters(cluster_states=[
            'RUNNING',
            'WAITING'
        ])

        self.assert_request_parameters({
            'Action': 'ListClusters',
            'ClusterStates.member.1': 'RUNNING',
            'ClusterStates.member.2': 'WAITING',
            'Version': '2009-03-31'
        })


class TestListInstanceGroups(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<ListInstanceGroupsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
  <ListInstanceGroupsResult>
    <InstanceGroups>
      <member>
        <Id>ig-aaaaaaaaaaaaa</Id>
        <InstanceType>m1.large</InstanceType>
        <Market>ON_DEMAND</Market>
        <Status>
          <StateChangeReason>
            <Message>Job flow terminated</Message>
            <Code>CLUSTER_TERMINATED</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:08Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <Name>Master instance group</Name>
        <RequestedInstanceCount>1</RequestedInstanceCount>
        <RunningInstanceCount>0</RunningInstanceCount>
        <InstanceGroupType>MASTER</InstanceGroupType>
      </member>
      <member>
        <Id>ig-aaaaaaaaaaab</Id>
        <InstanceType>m1.large</InstanceType>
        <Market>ON_DEMAND</Market>
        <Status>
          <StateChangeReason>
            <Message>Job flow terminated</Message>
            <Code>CLUSTER_TERMINATED</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:26Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <Name>Core instance group</Name>
        <RequestedInstanceCount>2</RequestedInstanceCount>
        <RunningInstanceCount>0</RunningInstanceCount>
        <InstanceGroupType>CORE</InstanceGroupType>
      </member>
    </InstanceGroups>
  </ListInstanceGroupsResult>
  <ResponseMetadata>
    <RequestId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</RequestId>
  </ResponseMetadata>
</ListInstanceGroupsResponse>

"""

    def test_list_instance_groups(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.list_instance_groups()

        response = self.service_connection.list_instance_groups(cluster_id='j-123')

        self.assert_request_parameters({
            'Action': 'ListInstanceGroups',
            'ClusterId': 'j-123',
            'Version': '2009-03-31'
        })

        self.assertTrue(isinstance(response, InstanceGroupList))
        self.assertEqual(len(response.instancegroups), 2)
        self.assertTrue(isinstance(response.instancegroups[0], InstanceGroupInfo))
        self.assertEqual(response.instancegroups[0].id, 'ig-aaaaaaaaaaaaa')
        self.assertEqual(response.instancegroups[0].instancegrouptype, "MASTER")
        self.assertEqual(response.instancegroups[0].instancetype, "m1.large")
        self.assertEqual(response.instancegroups[0].market, "ON_DEMAND")
        self.assertEqual(response.instancegroups[0].name, "Master instance group")
        self.assertEqual(response.instancegroups[0].requestedinstancecount, '1')
        self.assertEqual(response.instancegroups[0].runninginstancecount, '0')
        self.assertTrue(isinstance(response.instancegroups[0].status, ClusterStatus))
        self.assertEqual(response.instancegroups[0].status.state, 'TERMINATED')
        # status.statechangereason is not parsed into an object
        #self.assertEqual(response.instancegroups[0].status.statechangereason.code, 'CLUSTER_TERMINATED')

class TestListInstances(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<ListInstancesResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
  <ListInstancesResult>
    <Instances>
      <member>
        <Id>ci-123456789abc</Id>
        <Status>
          <StateChangeReason>
            <Message>Cluster was terminated.</Message>
            <Code>CLUSTER_TERMINATED</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:26Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:25Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <PrivateDnsName>ip-10-0-0-60.us-west-1.compute.internal</PrivateDnsName>
        <PublicIpAddress>54.0.0.1</PublicIpAddress>
        <PublicDnsName>ec2-54-0-0-1.us-west-1.compute.amazonaws.com</PublicDnsName>
        <Ec2InstanceId>i-aaaaaaaa</Ec2InstanceId>
        <PrivateIpAddress>10.0.0.60</PrivateIpAddress>
      </member>
      <member>
        <Id>ci-123456789abd</Id>
        <Status>
          <StateChangeReason>
            <Message>Cluster was terminated.</Message>
            <Code>CLUSTER_TERMINATED</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:26Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:25Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <PrivateDnsName>ip-10-0-0-61.us-west-1.compute.internal</PrivateDnsName>
        <PublicIpAddress>54.0.0.2</PublicIpAddress>
        <PublicDnsName>ec2-54-0-0-2.us-west-1.compute.amazonaws.com</PublicDnsName>
        <Ec2InstanceId>i-aaaaaaab</Ec2InstanceId>
        <PrivateIpAddress>10.0.0.61</PrivateIpAddress>
      </member>
      <member>
        <Id>ci-123456789abe3</Id>
        <Status>
          <StateChangeReason>
            <Message>Cluster was terminated.</Message>
            <Code>CLUSTER_TERMINATED</Code>
          </StateChangeReason>
          <State>TERMINATED</State>
          <Timeline>
            <CreationDateTime>2014-01-24T01:21:33Z</CreationDateTime>
            <ReadyDateTime>2014-01-24T01:25:08Z</ReadyDateTime>
            <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
          </Timeline>
        </Status>
        <PrivateDnsName>ip-10-0-0-62.us-west-1.compute.internal</PrivateDnsName>
        <PublicIpAddress>54.0.0.3</PublicIpAddress>
        <PublicDnsName>ec2-54-0-0-3.us-west-1.compute.amazonaws.com</PublicDnsName>
        <Ec2InstanceId>i-aaaaaaac</Ec2InstanceId>
        <PrivateIpAddress>10.0.0.62</PrivateIpAddress>
      </member>
    </Instances>
  </ListInstancesResult>
  <ResponseMetadata>
    <RequestId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</RequestId>
  </ResponseMetadata>
</ListInstancesResponse>
        """

    def test_list_instances(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.list_instances()

        response = self.service_connection.list_instances(cluster_id='j-123')
        self.assertTrue(isinstance(response, InstanceList))
        self.assertEqual(len(response.instances), 3)
        self.assertTrue(isinstance(response.instances[0], InstanceInfo))
        self.assertEqual(response.instances[0].ec2instanceid, 'i-aaaaaaaa')
        self.assertEqual(response.instances[0].id, 'ci-123456789abc')
        self.assertEqual(response.instances[0].privatednsname , 'ip-10-0-0-60.us-west-1.compute.internal')
        self.assertEqual(response.instances[0].privateipaddress , '10.0.0.60')
        self.assertEqual(response.instances[0].publicdnsname , 'ec2-54-0-0-1.us-west-1.compute.amazonaws.com')
        self.assertEqual(response.instances[0].publicipaddress , '54.0.0.1')


        self.assert_request_parameters({
            'Action': 'ListInstances',
            'ClusterId': 'j-123',
            'Version': '2009-03-31'
        })

    def test_list_instances_with_group_id(self):
        self.set_http_response(200)
        response = self.service_connection.list_instances(
            cluster_id='j-123', instance_group_id='abc')

        self.assert_request_parameters({
            'Action': 'ListInstances',
            'ClusterId': 'j-123',
            'InstanceGroupId': 'abc',
            'Version': '2009-03-31'
        })

    def test_list_instances_with_types(self):
        self.set_http_response(200)

        response = self.service_connection.list_instances(
            cluster_id='j-123', instance_group_types=[
                'MASTER',
                'TASK'
            ])

        self.assert_request_parameters({
            'Action': 'ListInstances',
            'ClusterId': 'j-123',
            'InstanceGroupTypeList.member.1': 'MASTER',
            'InstanceGroupTypeList.member.2': 'TASK',
            'Version': '2009-03-31'
        })


class TestListSteps(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """<ListStepsOutput><Steps><member><Name>Step 1</Name></member></Steps></ListStepsOutput>"""

    def test_list_steps(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.list_steps()

        response = self.service_connection.list_steps(cluster_id='j-123')

        self.assert_request_parameters({
            'Action': 'ListSteps',
            'ClusterId': 'j-123',
            'Version': '2009-03-31'
        })
        self.assertTrue(isinstance(response, StepSummaryList))
        self.assertEqual(response.steps[0].name, 'Step 1')

    def test_list_steps_with_states(self):
        self.set_http_response(200)
        response = self.service_connection.list_steps(
            cluster_id='j-123', step_states=[
                'COMPLETED',
                'FAILED'
            ])

        self.assert_request_parameters({
            'Action': 'ListSteps',
            'ClusterId': 'j-123',
            'StepStateList.member.1': 'COMPLETED',
            'StepStateList.member.2': 'FAILED',
            'Version': '2009-03-31'
        })
        self.assertTrue(isinstance(response, StepSummaryList))
        self.assertEqual(response.steps[0].name, 'Step 1')

class TestListBootstrapActions(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """<ListBootstrapActionsOutput></ListBootstrapActionsOutput>"""

    def test_list_bootstrap_actions(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.list_bootstrap_actions()

        response = self.service_connection.list_bootstrap_actions(cluster_id='j-123')

        self.assert_request_parameters({
            'Action': 'ListBootstrapActions',
            'ClusterId': 'j-123',
            'Version': '2009-03-31'
        })


class TestDescribeCluster(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<DescribeClusterResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
  <DescribeClusterResult>
    <Cluster>
      <Id>j-aaaaaaaaa</Id>
      <Tags/>
      <Ec2InstanceAttributes>
        <Ec2AvailabilityZone>us-west-1c</Ec2AvailabilityZone>
        <Ec2KeyName>my_secret_key</Ec2KeyName>
      </Ec2InstanceAttributes>
      <RunningAmiVersion>2.4.2</RunningAmiVersion>
      <VisibleToAllUsers>true</VisibleToAllUsers>
      <Status>
        <StateChangeReason>
          <Message>Terminated by user request</Message>
          <Code>USER_REQUEST</Code>
        </StateChangeReason>
        <State>TERMINATED</State>
        <Timeline>
          <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
          <ReadyDateTime>2014-01-24T01:25:26Z</ReadyDateTime>
          <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
        </Timeline>
      </Status>
      <AutoTerminate>false</AutoTerminate>
      <Name>test analytics</Name>
      <RequestedAmiVersion>2.4.2</RequestedAmiVersion>
      <Applications>
        <member>
          <Name>hadoop</Name>
          <Version>1.0.3</Version>
        </member>
      </Applications>
      <TerminationProtected>false</TerminationProtected>
    </Cluster>
  </DescribeClusterResult>
  <ResponseMetadata>
    <RequestId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</RequestId>
  </ResponseMetadata>
</DescribeClusterResponse>
        """

    def test_describe_cluster(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.describe_cluster()

        response = self.service_connection.describe_cluster(cluster_id='j-123')

        self.assertTrue(isinstance(response, Cluster))
        self.assertEqual(response.id, 'j-aaaaaaaaa')
        self.assertEqual(response.runningamiversion, '2.4.2')
        self.assertEqual(response.visibletoallusers, 'true')
        self.assertEqual(response.autoterminate, 'false')
        self.assertEqual(response.name, 'test analytics')
        self.assertEqual(response.requestedamiversion, '2.4.2')
        self.assertEqual(response.terminationprotected, 'false')
        self.assertEqual(response.ec2instanceattributes.ec2availabilityzone, "us-west-1c")
        self.assertEqual(response.ec2instanceattributes.ec2keyname, 'my_secret_key')
        self.assertEqual(response.status.state, 'TERMINATED')
        self.assertEqual(response.applications[0].name, 'hadoop')
        self.assertEqual(response.applications[0].version, '1.0.3')

        self.assert_request_parameters({
            'Action': 'DescribeCluster',
            'ClusterId': 'j-123',
            'Version': '2009-03-31'
        })


class TestDescribeStep(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """<DescribeStepOutput></DescribeStepOutput>"""

    def test_describe_step(self):
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.describe_step()

        with self.assertRaises(TypeError):
            self.service_connection.describe_step(cluster_id='j-123')

        with self.assertRaises(TypeError):
            self.service_connection.describe_step(step_id='abc')

        response = self.service_connection.describe_step(
            cluster_id='j-123', step_id='abc')

        self.assert_request_parameters({
            'Action': 'DescribeStep',
            'ClusterId': 'j-123',
            'StepId': 'abc',
            'Version': '2009-03-31'
        })


class TestAddJobFlowSteps(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<AddJobFlowStepsOutput>
    <StepIds>
        <member>Foo</member>
        <member>Bar</member>
    </StepIds>
</AddJobFlowStepsOutput>
"""

    def test_add_jobflow_steps(self):
        self.set_http_response(200)

        response = self.service_connection.add_jobflow_steps(
            jobflow_id='j-123', steps=[])

        # Make sure the correct object is returned, as this was
        # previously set to incorrectly return an empty instance
        # of RunJobFlowResponse.
        self.assertTrue(isinstance(response, JobFlowStepList))
        self.assertEqual(response.stepids[0].value, 'Foo')
        self.assertEqual(response.stepids[1].value, 'Bar')


class TestBuildTagList(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def test_key_without_value_encoding(self):
        input_dict = {
            'KeyWithNoValue': '',
            'AnotherKeyWithNoValue': None
        }
        res = self.service_connection._build_tag_list(input_dict)
        # Keys are outputted in ascending key order.
        expected = {
            'Tags.member.1.Key': 'AnotherKeyWithNoValue',
            'Tags.member.2.Key': 'KeyWithNoValue'
        }
        self.assertEqual(expected, res)

    def test_key_full_key_value_encoding(self):
        input_dict = {
            'FirstKey': 'One',
            'SecondKey': 'Two'
        }
        res = self.service_connection._build_tag_list(input_dict)
        # Keys are outputted in ascending key order.
        expected = {
            'Tags.member.1.Key': 'FirstKey',
            'Tags.member.1.Value': 'One',
            'Tags.member.2.Key': 'SecondKey',
            'Tags.member.2.Value': 'Two'
        }
        self.assertEqual(expected, res)


class TestAddTag(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """<AddTagsResponse
               xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
                   <AddTagsResult/>
                   <ResponseMetadata>
                        <RequestId>88888888-8888-8888-8888-888888888888</RequestId>
                   </ResponseMetadata>
               </AddTagsResponse>
               """

    def test_add_mix_of_tags_with_without_values(self):
        input_tags = {
            'FirstKey': 'One',
            'SecondKey': 'Two',
            'ZzzNoValue': ''
        }
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.add_tags()

        with self.assertRaises(TypeError):
            self.service_connection.add_tags('j-123')

        with self.assertRaises(AssertionError):
            self.service_connection.add_tags('j-123', [])

        response = self.service_connection.add_tags('j-123', input_tags)

        self.assertTrue(response)
        self.assert_request_parameters({
            'Action': 'AddTags',
            'ResourceId': 'j-123',
            'Tags.member.1.Key': 'FirstKey',
            'Tags.member.1.Value': 'One',
            'Tags.member.2.Key': 'SecondKey',
            'Tags.member.2.Value': 'Two',
            'Tags.member.3.Key': 'ZzzNoValue',
            'Version': '2009-03-31'
        })


class TestRemoveTag(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """<RemoveTagsResponse
               xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
                   <RemoveTagsResult/>
                   <ResponseMetadata>
                        <RequestId>88888888-8888-8888-8888-888888888888</RequestId>
                   </ResponseMetadata>
               </RemoveTagsResponse>
               """

    def test_remove_tags(self):
        input_tags = {
            'FirstKey': 'One',
            'SecondKey': 'Two',
            'ZzzNoValue': ''
        }
        self.set_http_response(200)

        with self.assertRaises(TypeError):
            self.service_connection.add_tags()

        with self.assertRaises(TypeError):
            self.service_connection.add_tags('j-123')

        with self.assertRaises(AssertionError):
            self.service_connection.add_tags('j-123', [])

        response = self.service_connection.remove_tags('j-123', ['FirstKey', 'SecondKey'])

        self.assertTrue(response)
        self.assert_request_parameters({
            'Action': 'RemoveTags',
            'ResourceId': 'j-123',
            'TagKeys.member.1': 'FirstKey',
            'TagKeys.member.2': 'SecondKey',
            'Version': '2009-03-31'
        })

class DescribeJobFlowsTestBase(AWSMockServiceTestCase):
    connection_class = EmrConnection

    def default_body(self):
        return """
<DescribeJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
  <DescribeJobFlowsResult>
    <JobFlows>
      <member>
        <AmiVersion>2.4.2</AmiVersion>
        <ExecutionStatusDetail>
          <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
          <LastStateChangeReason>Terminated by user request</LastStateChangeReason>
          <StartDateTime>2014-01-24T01:25:26Z</StartDateTime>
          <ReadyDateTime>2014-01-24T01:25:26Z</ReadyDateTime>
          <State>TERMINATED</State>
          <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
        </ExecutionStatusDetail>
        <BootstrapActions/>
        <VisibleToAllUsers>true</VisibleToAllUsers>
        <SupportedProducts/>
        <Name>test analytics</Name>
        <JobFlowId>j-aaaaaa</JobFlowId>
        <Steps>
          <member>
            <ExecutionStatusDetail>
              <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
              <StartDateTime>2014-01-24T01:25:26Z</StartDateTime>
              <State>COMPLETED</State>
              <EndDateTime>2014-01-24T01:26:08Z</EndDateTime>
            </ExecutionStatusDetail>
            <StepConfig>
              <HadoopJarStep>
                <Args>
                  <member>s3://us-west-1.elasticmapreduce/libs/hive/hive-script</member>
                  <member>--base-path</member>
                  <member>s3://us-west-1.elasticmapreduce/libs/hive/</member>
                  <member>--install-hive</member>
                  <member>--hive-versions</member>
                  <member>0.11.0.1</member>
                </Args>
                <Jar>s3://us-west-1.elasticmapreduce/libs/script-runner/script-runner.jar</Jar>
                <Properties/>
              </HadoopJarStep>
              <Name>Setup hive</Name>
              <ActionOnFailure>TERMINATE_JOB_FLOW</ActionOnFailure>
            </StepConfig>
          </member>
        </Steps>
        <Instances>
          <Placement>
            <AvailabilityZone>us-west-1c</AvailabilityZone>
          </Placement>
          <MasterInstanceType>m1.large</MasterInstanceType>
          <Ec2KeyName>my_key</Ec2KeyName>
          <KeepJobFlowAliveWhenNoSteps>true</KeepJobFlowAliveWhenNoSteps>
          <InstanceGroups>
            <member>
              <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
              <InstanceRunningCount>0</InstanceRunningCount>
              <StartDateTime>2014-01-24T01:23:56Z</StartDateTime>
              <ReadyDateTime>2014-01-24T01:25:08Z</ReadyDateTime>
              <State>ENDED</State>
              <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
              <InstanceRequestCount>1</InstanceRequestCount>
              <InstanceType>m1.large</InstanceType>
              <LastStateChangeReason>Job flow terminated</LastStateChangeReason>
              <Market>ON_DEMAND</Market>
              <InstanceGroupId>ig-aaaaaa</InstanceGroupId>
              <InstanceRole>MASTER</InstanceRole>
              <Name>Master instance group</Name>
            </member>
            <member>
              <CreationDateTime>2014-01-24T01:21:21Z</CreationDateTime>
              <InstanceRunningCount>0</InstanceRunningCount>
              <StartDateTime>2014-01-24T01:25:26Z</StartDateTime>
              <ReadyDateTime>2014-01-24T01:25:26Z</ReadyDateTime>
              <State>ENDED</State>
              <EndDateTime>2014-01-24T02:19:46Z</EndDateTime>
              <InstanceRequestCount>2</InstanceRequestCount>
              <InstanceType>m1.large</InstanceType>
              <LastStateChangeReason>Job flow terminated</LastStateChangeReason>
              <Market>ON_DEMAND</Market>
              <InstanceGroupId>ig-aaaaab</InstanceGroupId>
              <InstanceRole>CORE</InstanceRole>
              <Name>Core instance group</Name>
            </member>
          </InstanceGroups>
          <SlaveInstanceType>m1.large</SlaveInstanceType>
          <MasterInstanceId>i-aaaaaa</MasterInstanceId>
          <HadoopVersion>1.0.3</HadoopVersion>
          <NormalizedInstanceHours>12</NormalizedInstanceHours>
          <MasterPublicDnsName>ec2-184-0-0-1.us-west-1.compute.amazonaws.com</MasterPublicDnsName>
          <InstanceCount>3</InstanceCount>
          <TerminationProtected>false</TerminationProtected>
        </Instances>
      </member>
    </JobFlows>
  </DescribeJobFlowsResult>
  <ResponseMetadata>
    <RequestId>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</RequestId>
  </ResponseMetadata>
</DescribeJobFlowsResponse>
        """

class TestDescribeJobFlows(DescribeJobFlowsTestBase):

    def test_describe_jobflows_response(self):
        self.set_http_response(200)

        response = self.service_connection.describe_jobflows()
        self.assertTrue(isinstance(response, list))

        jf = response[0]
        self.assertTrue(isinstance(jf, JobFlow))
        self.assertEqual(jf.amiversion, '2.4.2')
        self.assertEqual(jf.visibletoallusers, 'true')
        self.assertEqual(jf.name, 'test analytics')
        self.assertEqual(jf.jobflowid, 'j-aaaaaa')
        self.assertEqual(jf.ec2keyname, 'my_key')
        self.assertEqual(jf.masterinstancetype, 'm1.large')
        self.assertEqual(jf.availabilityzone, 'us-west-1c')
        self.assertEqual(jf.keepjobflowalivewhennosteps, 'true')
        self.assertEqual(jf.slaveinstancetype, 'm1.large')
        self.assertEqual(jf.masterinstanceid, 'i-aaaaaa')
        self.assertEqual(jf.hadoopversion, '1.0.3')
        self.assertEqual(jf.normalizedinstancehours, '12')
        self.assertEqual(jf.masterpublicdnsname, 'ec2-184-0-0-1.us-west-1.compute.amazonaws.com')
        self.assertEqual(jf.instancecount, '3')
        self.assertEqual(jf.terminationprotected, 'false')

        self.assertTrue(isinstance(jf.steps, list))
        step = jf.steps[0]
        self.assertTrue(isinstance(step, Step))
        self.assertEqual(step.jar, 's3://us-west-1.elasticmapreduce/libs/script-runner/script-runner.jar')
        self.assertEqual(step.name, 'Setup hive')
        self.assertEqual(step.actiononfailure, 'TERMINATE_JOB_FLOW')

        self.assertTrue(isinstance(jf.instancegroups, list))
        ig = jf.instancegroups[0]
        self.assertTrue(isinstance(ig, InstanceGroup))
        self.assertEqual(ig.creationdatetime, '2014-01-24T01:21:21Z')
        self.assertEqual(ig.state, 'ENDED')
        self.assertEqual(ig.instancerequestcount, '1')
        self.assertEqual(ig.instancetype, 'm1.large')
        self.assertEqual(ig.laststatechangereason, 'Job flow terminated')
        self.assertEqual(ig.market, 'ON_DEMAND')
        self.assertEqual(ig.instancegroupid, 'ig-aaaaaa')
        self.assertEqual(ig.instancerole, 'MASTER')
        self.assertEqual(ig.name, 'Master instance group')

    def test_describe_jobflows_no_args(self):
        self.set_http_response(200)

        self.service_connection.describe_jobflows()

        self.assert_request_parameters({
            'Action': 'DescribeJobFlows',
        }, ignore_params_values=['Version'])

    def test_describe_jobflows_filtered(self):
        self.set_http_response(200)

        now = datetime.now()
        a_bit_before = datetime.fromtimestamp(time() - 1000)

        self.service_connection.describe_jobflows(states=['WAITING', 'RUNNING'], jobflow_ids=['j-aaaaaa', 'j-aaaaab'], created_after=a_bit_before, created_before=now)
        self.assert_request_parameters({
            'Action': 'DescribeJobFlows',
            'JobFlowIds.member.1': 'j-aaaaaa',
            'JobFlowIds.member.2': 'j-aaaaab',
            'JobFlowStates.member.1': 'WAITING',
            'JobFlowStates.member.2': 'RUNNING',
            'CreatedAfter': a_bit_before.strftime(boto.utils.ISO8601),
            'CreatedBefore': now.strftime(boto.utils.ISO8601),
        }, ignore_params_values=['Version'])

class TestDescribeJobFlow(DescribeJobFlowsTestBase):
    def test_describe_jobflow(self):
        self.set_http_response(200)

        response = self.service_connection.describe_jobflow('j-aaaaaa')
        self.assertTrue(isinstance(response, JobFlow))
        self.assert_request_parameters({
            'Action': 'DescribeJobFlows',
            'JobFlowIds.member.1': 'j-aaaaaa',
        }, ignore_params_values=['Version'])
