summaryrefslogtreecommitdiffstats
path: root/testing/mozharness/mozharness/mozilla/bouncer/submitter.py
blob: e9289d2be6721463cb0147869c09bea8b0887b21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import base64
import socket
import sys
import traceback
from xml.dom.minidom import parseString

from mozharness.base.log import FATAL

try:
    import httplib
except ImportError:
    import http.client as httplib
try:
    from urllib import quote, urlencode
except ImportError:
    from urllib.parse import quote, urlencode
try:
    from urllib2 import HTTPError, Request, URLError, urlopen
except ImportError:
    from urllib.request import HTTPError, Request, URLError, urlopen


class BouncerSubmitterMixin(object):
    def query_credentials(self):
        if self.credentials:
            return self.credentials
        global_dict = {}
        local_dict = {}
        exec(
            compile(
                open(self.config["credentials_file"], "rb").read(),
                self.config["credentials_file"],
                "exec",
            ),
            global_dict,
            local_dict,
        )
        self.credentials = (local_dict["tuxedoUsername"], local_dict["tuxedoPassword"])
        return self.credentials

    def api_call(self, route, data, error_level=FATAL, retry_config=None):
        retry_args = dict(
            failure_status=None,
            retry_exceptions=(
                HTTPError,
                URLError,
                httplib.BadStatusLine,
                socket.timeout,
                socket.error,
            ),
            error_message="call to %s failed" % (route),
            error_level=error_level,
        )

        if retry_config:
            retry_args.update(retry_config)

        return self.retry(self._api_call, args=(route, data), **retry_args)

    def _api_call(self, route, data):
        api_prefix = self.config["bouncer-api-prefix"]
        api_url = "%s/%s" % (api_prefix, route)
        request = Request(api_url)
        if data:
            post_data = urlencode(data, doseq=True)
            request.add_data(post_data)
            self.info("POST data: %s" % post_data)
        credentials = self.query_credentials()
        if credentials:
            auth = base64.encodestring("%s:%s" % credentials)
            request.add_header("Authorization", "Basic %s" % auth.strip())
        try:
            self.info("Submitting to %s" % api_url)
            res = urlopen(request, timeout=60).read()
            self.info("Server response")
            self.info(res)
            return res
        except HTTPError as e:
            self.warning("Cannot access %s" % api_url)
            traceback.print_exc(file=sys.stdout)
            self.warning("Returned page source:")
            self.warning(e.read())
            raise
        except URLError:
            traceback.print_exc(file=sys.stdout)
            self.warning("Cannot access %s" % api_url)
            raise
        except socket.timeout as e:
            self.warning("Timed out accessing %s: %s" % (api_url, e))
            raise
        except socket.error as e:
            self.warning("Socket error when accessing %s: %s" % (api_url, e))
            raise
        except httplib.BadStatusLine as e:
            self.warning("BadStatusLine accessing %s: %s" % (api_url, e))
            raise

    def product_exists(self, product_name):
        self.info("Checking if %s already exists" % product_name)
        res = self.api_call("product_show?product=%s" % quote(product_name), data=None)
        try:
            xml = parseString(res)
            # API returns <products/> if the product doesn't exist
            products_found = len(xml.getElementsByTagName("product"))
            self.info("Products found: %s" % products_found)
            return bool(products_found)
        except Exception as e:
            self.warning("Error parsing XML: %s" % e)
            self.warning("Assuming %s does not exist" % product_name)
            # ignore XML parsing errors
            return False

    def api_add_product(self, product_name, add_locales, ssl_only=False):
        data = {
            "product": product_name,
        }
        if self.locales and add_locales:
            data["languages"] = self.locales
        if ssl_only:
            # Send "true" as a string
            data["ssl_only"] = "true"
        self.api_call("product_add/", data)

    def api_add_location(self, product_name, bouncer_platform, path):
        data = {
            "product": product_name,
            "os": bouncer_platform,
            "path": path,
        }
        self.api_call("location_add/", data)