1
# This file is part of Buildbot.  Buildbot is free software: you can
2
# redistribute it and/or modify it under the terms of the GNU General Public
3
# License as published by the Free Software Foundation, version 2.
4
#
5
# This program is distributed in the hope that it will be useful, but WITHOUT
6
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8
# details.
9
#
10
# You should have received a copy of the GNU General Public License along with
11
# this program; if not, write to the Free Software Foundation, Inc., 51
12
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13
#
14
# Copyright Buildbot Team Members
15

16

17 4
"""Base classes handy for use with PB clients.
18
"""
19

20 4
from __future__ import absolute_import
21 4
from __future__ import print_function
22 4
from future.utils import iteritems
23

24 4
from twisted.cred import error
25 4
from twisted.internet import reactor
26 4
from twisted.python import log
27 4
from twisted.spread import pb
28 4
from twisted.spread.pb import PBClientFactory
29

30 4
from buildbot_worker.compat import bytes2unicode
31

32

33 4
class AutoLoginPBFactory(PBClientFactory):
34
    """Factory for PB brokers that are managed through a ClientService.
35

36
    Upon reconnect issued by ClientService this factory will re-login.
37

38
    Instead of using f.getRootObject (which gives a Deferred that can only
39
    be fired once), override the gotRootObject method. GR -> yes in case a user
40
    would use that to be notified of root object appearances, it wouldn't
41
    work. But getRootObject() can itself be used as much as one wants.
42

43
    Instead of using the f.login (which is also one-shot), call
44
    f.startLogin() with the credentials and client, and override the
45
    gotPerspective method.
46

47
    gotRootObject and gotPerspective will be called each time the object is
48
    received (once per successful connection attempt).
49

50
    If an authorization error occurs, failedToGetPerspective() will be
51
    invoked.
52
    """
53

54 4
    def clientConnectionMade(self, broker):
55 4
        PBClientFactory.clientConnectionMade(self, broker)
56 4
        self.doLogin(self._root, broker)
57 4
        self.gotRootObject(self._root)
58

59 4
    def login(self, *args):
60 0
        raise RuntimeError("login is one-shot: use startLogin instead")
61

62 4
    def startLogin(self, credentials, client=None):
63 4
        self._credentials = credentials
64 4
        self._client = client
65

66 4
    def doLogin(self, root, broker):
67 4
        d = self._cbSendUsername(root, self._credentials.username,
68
                                 self._credentials.password, self._client)
69 4
        d.addCallbacks(self.gotPerspective, self.failedToGetPerspective,
70
                       errbackArgs=(broker,))
71 4
        return d
72

73
    # methods to override
74

75 4
    def gotPerspective(self, perspective):
76
        """The remote avatar or perspective (obtained each time this factory
77
        connects) is now available."""
78

79 4
    def gotRootObject(self, root):
80
        """The remote root object (obtained each time this factory connects)
81
        is now available. This method will be called each time the connection
82
        is established and the object reference is retrieved."""
83

84 4
    def failedToGetPerspective(self, why, broker):
85
        """The login process failed, most likely because of an authorization
86
        failure (bad password), but it is also possible that we lost the new
87
        connection before we managed to send our credentials.
88
        """
89 4
        log.msg("ReconnectingPBClientFactory.failedToGetPerspective")
90
        # put something useful in the logs
91 4
        if why.check(pb.PBConnectionLost):
92 4
            log.msg("we lost the brand-new connection")
93
            # fall through
94 4
        elif why.check(error.UnauthorizedLogin):
95 4
            log.msg("unauthorized login; check worker name and password")
96
            # fall through
97
        else:
98 0
            log.err(why, 'While trying to connect:')
99 0
            reactor.stop()
100 0
            return
101

102
        # lose the current connection, which will trigger a retry
103 4
        broker.transport.loseConnection()
104

105

106 4
def decode(data, encoding='utf-8', errors='strict'):
107
    """We need to convert a dictionary where keys and values
108
    are bytes, to unicode strings.  This happens when a
109
    Python 2 master sends a dictionary back to a Python 3 worker.
110
    """
111 4
    data_type = type(data)
112

113 4
    if data_type == bytes:
114 0
        return bytes2unicode(data, encoding, errors)
115 4
    if data_type in (dict, list, tuple):
116 4
        if data_type == dict:
117 4
            data = iteritems(data)
118 4
        return data_type(map(decode, data))
119 4
    return data

Read our documentation on viewing source code .

Loading