Example #1
0
    def test_json_content_missing_content_type(self):
        """
        When a request is made with the json_content callback and the
        content-type header is not set in the response headers then an error
        should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(json_content)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))

        request.setResponseCode(200)
        # Twisted will set the content type to "text/html" by default but this
        # can be disabled by setting the default content type to None:
        # https://twistedmatrix.com/documents/current/api/twisted.web.server.Request.html#defaultContentType
        request.defaultContentType = None
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Expected header "Content-Type" to be '
                       '"application/json" but header not found in response')))
Example #2
0
    def test_mlb_signal_hup(self):
        """
        When the marathon-lb client is used to send a SIGHUP signal to
        marathon-lb, all the correct API endpoints are called.
        """
        d = self.cleanup_d(self.client.mlb_signal_hup())

        for lb in ['lb1', 'lb2']:
            request = yield self.requests.get()
            self.assertThat(request, HasRequestProperties(
                method='POST', url='http://%s:9090/_mlb_signal/hup' % (lb,)))

            request.setResponseCode(200)
            request.setHeader('content-type', 'text/plain')
            request.write(b'Sent SIGHUP signal to marathon-lb')
            request.finish()

        responses = yield d
        self.assertThat(len(responses), Equals(2))
        for response in responses:
            self.assertThat(response.code, Equals(200))
            self.assertThat(response.headers, HasHeader(
                'content-type', ['text/plain']))

            response_text = yield response.text()
            self.assertThat(response_text,
                            Equals('Sent SIGHUP signal to marathon-lb'))
Example #3
0
    def test_json_content_incorrect_content_type(self):
        """
        When a request is made with the json_content callback and the
        content-type header is set to a value other than 'application/json' in
        the response headers then an error should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(json_content)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/octet-stream')
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "application/json" but '
            'found "application/octet-stream" instead')))
Example #4
0
    def test_get_events_non_200(self):
        """
        When a request is made to Marathon's event stream, and a non-200
        response code is returned, an error should be raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(202)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Non-200 response code (202) for url: '
                       'http://localhost:8080/v2/events')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
Example #5
0
    def test_get_events_incorrect_content_type(self):
        """
        When a request is made to Marathon's event stream, and the content-type
        header value returned is not "text/event-stream", an error should be
        raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/json')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "text/event-stream" but '
            'found "application/json" instead')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
Example #6
0
    def test_get_events(self):
        """
        When a request is made to Marathon's event stream, a callback should
        receive JSON-decoded data before the connection is closed.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(data, Equals([json_data]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
Example #7
0
    def test_request_partial_failure(self):
        """
        When a request is made and an error status code is returned from some
        (but not all) of the matathon-lb instances, then the request returns
        the list of responses with a None value for the unhappy request.
        """
        d = self.cleanup_d(self.client.request('GET', path='/my-path'))

        lb1_request = yield self.requests.get()
        self.assertThat(lb1_request, HasRequestProperties(
            method='GET', url='http://lb1:9090/my-path'))

        lb2_request = yield self.requests.get()
        self.assertThat(lb2_request, HasRequestProperties(
            method='GET', url='http://lb2:9090/my-path'))

        # Fail the first one
        lb1_request.setResponseCode(500)
        lb1_request.setHeader('content-type', 'text/plain')
        lb1_request.write(b'Internal Server Error')
        lb1_request.finish()

        # ...but succeed the second
        lb2_request.setResponseCode(200)
        lb2_request.setHeader('content-type', 'text/plain')
        lb2_request.write(b'Yes, I work')
        lb2_request.finish()

        responses = yield d
        self.assertThat(responses, HasLength(2))
        lb1_response, lb2_response = responses

        self.assertThat(lb1_response, Is(None))
        self.assertThat(lb2_response, MatchesStructure(
            code=Equals(200),
            headers=HasHeader('content-type', ['text/plain'])
        ))

        lb2_response_content = yield lb2_response.content()
        self.assertThat(lb2_response_content, Equals(b'Yes, I work'))

        flush_logged_errors(HTTPError)
Example #8
0
    def test_params_precedence_over_url_query(self):
        """
        When query parameters are specified as both the params kwarg and in the
        URL, the params kwarg takes precedence.
        """
        self.cleanup_d(self.client.request(
            'GET', self.uri('/hello?from=mars'), params={'from': 'earth'}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello'), query={'from': ['earth']}))

        request.setResponseCode(200)
        request.finish()
Example #9
0
    def test_request_url(self):
        """
        When a request is made with the url parameter set, that parameter
        should be used as the base URL.
        """
        self.cleanup_d(self.client.request(
            'GET', path='/hello', url='http://localhost:9000'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url='http://localhost:9000/hello'))

        request.setResponseCode(200)
        request.finish()
Example #10
0
    def test_url_query_as_params(self):
        """
        When query parameters are specified in the URL, those parameters are
        reflected in the request.
        """
        self.cleanup_d(self.client.request(
            'GET', self.uri('/hello?from=earth')))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello'), query={'from': ['earth']}))

        request.setResponseCode(200)
        request.finish()
Example #11
0
    def test_get_apps(self):
        """
        When we request the list of apps from Marathon, we should receive the
        list of apps with some information.
        """
        d = self.cleanup_d(self.client.get_apps())

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/apps')))

        apps = {
            'apps': [
                {
                    'id': '/product/us-east/service/myapp',
                    'cmd': 'env && sleep 60',
                    'constraints': [
                        [
                            'hostname',
                            'UNIQUE',
                            ''
                        ]
                    ],
                    'container': None,
                    'cpus': 0.1,
                    'env': {
                        'LD_LIBRARY_PATH': '/usr/local/lib/myLib'
                    },
                    'executor': '',
                    'instances': 3,
                    'mem': 5.0,
                    'ports': [
                        15092,
                        14566
                    ],
                    'tasksRunning': 0,
                    'tasksStaged': 1,
                    'uris': [
                        'https://raw.github.com/mesosphere/marathon/master/'
                        'README.md'
                    ],
                    'version': '2014-03-01T23:42:20.938Z'
                }
            ]
        }
        json_response(request, apps)

        res = yield d
        self.assertThat(res, Equals(apps['apps']))
Example #12
0
    def test_url_overrides(self):
        """
        When URL parts are overridden via keyword arguments, those overrides
        should be reflected in the request.
        """
        self.cleanup_d(self.client.request(
            'GET', 'http://example.com:8000/hello#section1',
            scheme='https', host='example2.com', port='9000', path='/goodbye',
            fragment='section2'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url='https://example2.com:9000/goodbye#section2'))

        request.setResponseCode(200)
        request.finish()
Example #13
0
    def test_auth(self):
        """
        When basic auth credentials are specified as the auth kwarg, the
        encoded credentials are present in the request headers.
        """
        self.cleanup_d(self.client.request(
            'GET', path='/hello', auth=('user', 'pa$$word')))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(
            request.requestHeaders,
            HasHeader('Authorization', ['Basic dXNlcjpwYSQkd29yZA==']))

        request.setResponseCode(200)
        request.finish()
Example #14
0
    def test_url_userinfo_as_auth(self):
        """
        When basic auth credentials are specified in the URL, the encoded
        credentials are present in the request headers.
        """
        self.cleanup_d(self.client.request(
            'GET', 'http://*****:*****@localhost:8000/hello'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(
            request.requestHeaders,
            HasHeader('Authorization', ['Basic dXNlcjpwYSQkd29yZA==']))

        request.setResponseCode(200)
        request.finish()
Example #15
0
    def test_auth_precedence_over_url_userinfo(self):
        """
        When basic auth credentials are specified as both the auth kwarg and in
        the URL, the credentials in the auth kwarg take precedence.
        """
        self.cleanup_d(self.client.request(
            'GET', 'http://*****:*****@localhost:8000/hello',
            auth=('user', 'pa$$word')))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(
            request.requestHeaders,
            HasHeader('Authorization', ['Basic dXNlcjpwYSQkd29yZA==']))

        request.setResponseCode(200)
        request.finish()
Example #16
0
    def test_get_json_field_error(self):
        """
        When get_json_field is used to make a request but the response code
        indicates an error, an HTTPError should be raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        request.setResponseCode(404)
        request.write(b'Not found\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '404 Client Error for url: %s' % self.uri('/my-path'))))
Example #17
0
    def test_request_success(self):
        """
        When a request is made, it is made to all marathon-lb instances and
        the responses are returned.
        """
        d = self.cleanup_d(self.client.request('GET', path='/my-path'))

        for lb in ['lb1', 'lb2']:
            request = yield self.requests.get()
            self.assertThat(request, HasRequestProperties(
                method='GET', url='http://%s:9090/my-path' % (lb,)))

            request.setResponseCode(200)
            request.finish()

        responses = yield d
        self.assertThat(responses, HasLength(2))
        for response in responses:
            self.assertThat(response.code, Equals(200))
Example #18
0
    def test_get_json_field(self):
        """
        When get_json_field is used to make a request, the response is
        deserialized from JSON and the value of the specified field is
        returned.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        json_response(request, {
            'field-key': 'field-value',
            'other-field-key': 'do-not-care'
        })

        res = yield d
        self.assertThat(res, Equals('field-value'))
Example #19
0
    def test_server_error_response(self):
        """
        When a request is made and the raise_for_status callback is added and a
        5xx response code is returned, a HTTPError should be raised to indicate
        a server error.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(raise_for_status)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))

        request.setResponseCode(502)
        request.write(b'Bad gateway\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '502 Server Error for url: %s' % self.uri('/hello'))))
Example #20
0
    def test_request_json_data(self):
        """
        When a request is made with the json_data parameter set, that data
        should be sent as JSON and the content-type header should be set to
        indicate this.
        """
        self.cleanup_d(self.client.request(
            'GET', path='/hello', json_data={'test': 'hello'}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders, HasHeader(
            'content-type', ['application/json']))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))
        self.assertThat(read_request_json(request), Equals({'test': 'hello'}))

        request.setResponseCode(200)
        request.finish()
Example #21
0
    def test_request(self):
        """
        When a request is made, it should be made with the correct method,
        address and headers, and should contain an empty body. The response
        should be returned.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.content.read(), Equals(b''))

        request.setResponseCode(200)
        request.write(b'hi\n')
        request.finish()

        response = yield d
        text = yield response.text()
        self.assertThat(text, Equals('hi\n'))
Example #22
0
    def test_get_events_multiple_callbacks(self):
        """
        When a request is made to Marathon's event stream, and there are
        events for multiple callbacks, those callbacks should receive
        JSON-decoded data for each event.
        """
        data1 = []
        data2 = []
        d = self.cleanup_d(self.client.get_events({
            'test1': data1.append,
            'test2': data2.append
        }))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data1 = {'hello': 'world'}
        request.write(b'event: test1\n')
        request.write(b'data: %s\n' % (json.dumps(json_data1).encode('utf-8')))
        request.write(b'\n')

        json_data2 = {'hello': 'computer'}
        request.write(b'event: test2\n')
        request.write(b'data: %s\n' % (json.dumps(json_data2).encode('utf-8')))
        request.write(b'\n')

        yield wait0()
        self.assertThat(data1, Equals([json_data1]))
        self.assertThat(data2, Equals([json_data2]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
Example #23
0
    def test_request_debug_log(self):
        """
        When a request is made in debug mode, things should run smoothly.
        (Don't really want to check the log output here, just that things don't
        break.)
        """
        self.client.debug = True
        d = self.cleanup_d(self.client.request('GET', path='/hello'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.content.read(), Equals(b''))

        request.setResponseCode(200)
        request.write(b'hi\n')
        request.finish()

        response = yield d
        text = yield response.text()
        self.assertThat(text, Equals('hi\n'))
Example #24
0
    def test_get_json_field_missing(self):
        """
        When get_json_field is used to make a request, the response is
        deserialized from JSON and if the specified field is missing, an error
        is raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        json_response(request, {'other-field-key': 'do-not-care'})

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            KeyError,
            '\'Unable to get value for "field-key" from Marathon response: '
            '"{"other-field-key": "do-not-care"}"\''
        )))
Example #25
0
    def test_request_success(self):
        """
        When we make a request and there are multiple Marathon endpoints
        specified, the first endpoint is used.
        """
        agent = PerLocationAgent()
        agent.add_agent(b'localhost:8080', self.fake_server.get_agent())
        agent.add_agent(b'localhost:9090', FailingAgent())
        client = MarathonClient(
            ['http://localhost:8080', 'http://localhost:9090'],
            client=treq_HTTPClient(agent))

        d = self.cleanup_d(client.request('GET', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url='http://localhost:8080/my-path'))

        request.setResponseCode(200)
        request.finish()

        yield d
Example #26
0
    def test_json_content(self):
        """
        When a request is made with the json_content callback and the
        'application/json' content type is set in the response headers then the
        JSON should be successfully parsed.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(json_content)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/json')
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        response = yield d
        self.assertThat(response, Equals({}))
Example #27
0
    def test_request_failure(self):
        """
        When the requests to all the marathon-lb instances have a bad status
        code then an error should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/my-path'))

        for lb in ['lb1', 'lb2']:
            request = yield self.requests.get()
            self.assertThat(request, HasRequestProperties(
                method='GET', url='http://%s:9090/my-path' % (lb,)))

            request.setResponseCode(500)
            request.setHeader('content-type', 'text/plain')
            request.write(b'Internal Server Error')
            request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            RuntimeError,
            'Failed to make a request to all marathon-lb instances'
        )))

        flush_logged_errors(HTTPError)