| 1 | """ |
|---|
| 2 | A storage server plugin the test suite can use to validate the |
|---|
| 3 | functionality. |
|---|
| 4 | |
|---|
| 5 | Ported to Python 3. |
|---|
| 6 | """ |
|---|
| 7 | |
|---|
| 8 | import attr |
|---|
| 9 | |
|---|
| 10 | from zope.interface import ( |
|---|
| 11 | implementer, |
|---|
| 12 | ) |
|---|
| 13 | |
|---|
| 14 | from twisted.internet.defer import ( |
|---|
| 15 | succeed, |
|---|
| 16 | ) |
|---|
| 17 | from twisted.web.resource import ( |
|---|
| 18 | Resource, |
|---|
| 19 | ) |
|---|
| 20 | from twisted.web.static import ( |
|---|
| 21 | Data, |
|---|
| 22 | ) |
|---|
| 23 | from foolscap.api import ( |
|---|
| 24 | RemoteInterface, |
|---|
| 25 | ) |
|---|
| 26 | |
|---|
| 27 | from allmydata.interfaces import ( |
|---|
| 28 | IFoolscapStoragePlugin, |
|---|
| 29 | IStorageServer, |
|---|
| 30 | ) |
|---|
| 31 | from allmydata.client import ( |
|---|
| 32 | AnnounceableStorageServer, |
|---|
| 33 | ) |
|---|
| 34 | from allmydata.util.jsonbytes import ( |
|---|
| 35 | dumps, |
|---|
| 36 | ) |
|---|
| 37 | |
|---|
| 38 | |
|---|
| 39 | class RIDummy(RemoteInterface): |
|---|
| 40 | __remote_name__ = "RIDummy.tahoe.allmydata.com" |
|---|
| 41 | |
|---|
| 42 | def just_some_method(): |
|---|
| 43 | """ |
|---|
| 44 | Just some method so there is something callable on this object. We won't |
|---|
| 45 | pretend to actually offer any storage capabilities. |
|---|
| 46 | """ |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | # type ignored due to missing stubs for Twisted |
|---|
| 50 | # https://twistedmatrix.com/trac/ticket/9717 |
|---|
| 51 | @implementer(IFoolscapStoragePlugin) # type: ignore |
|---|
| 52 | @attr.s |
|---|
| 53 | class DummyStorage: |
|---|
| 54 | name = attr.ib() |
|---|
| 55 | |
|---|
| 56 | @property |
|---|
| 57 | def _client_section_name(self): |
|---|
| 58 | return u"storageclient.plugins.{}".format(self.name) |
|---|
| 59 | |
|---|
| 60 | def get_storage_server(self, configuration, get_anonymous_storage_server): |
|---|
| 61 | if u"invalid" in configuration: |
|---|
| 62 | raise Exception("The plugin is unhappy.") |
|---|
| 63 | |
|---|
| 64 | announcement = {u"value": configuration.get(u"some", u"default-value")} |
|---|
| 65 | storage_server = DummyStorageServer(get_anonymous_storage_server) |
|---|
| 66 | return succeed( |
|---|
| 67 | AnnounceableStorageServer( |
|---|
| 68 | announcement, |
|---|
| 69 | storage_server, |
|---|
| 70 | ), |
|---|
| 71 | ) |
|---|
| 72 | |
|---|
| 73 | def get_storage_client(self, configuration, announcement, get_rref): |
|---|
| 74 | return DummyStorageClient( |
|---|
| 75 | get_rref, |
|---|
| 76 | dict(configuration.items(self._client_section_name, [])), |
|---|
| 77 | announcement, |
|---|
| 78 | ) |
|---|
| 79 | |
|---|
| 80 | def get_client_resource(self, configuration): |
|---|
| 81 | """ |
|---|
| 82 | :return: A static data resource that produces the given configuration when |
|---|
| 83 | rendered, as an aid to testing. |
|---|
| 84 | """ |
|---|
| 85 | items = configuration.items(self._client_section_name, []) |
|---|
| 86 | resource = Data( |
|---|
| 87 | dumps(dict(items)).encode("utf-8"), |
|---|
| 88 | "text/json", |
|---|
| 89 | ) |
|---|
| 90 | # Give it some dynamic stuff too. |
|---|
| 91 | resource.putChild(b"counter", GetCounter()) |
|---|
| 92 | return resource |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | class GetCounter(Resource, object): |
|---|
| 96 | """ |
|---|
| 97 | ``GetCounter`` is a resource that returns a count of the number of times |
|---|
| 98 | it has rendered a response to a GET request. |
|---|
| 99 | |
|---|
| 100 | :ivar int value: The number of ``GET`` requests rendered so far. |
|---|
| 101 | """ |
|---|
| 102 | value = 0 |
|---|
| 103 | def render_GET(self, request): |
|---|
| 104 | self.value += 1 |
|---|
| 105 | return dumps({"value": self.value}).encode("utf-8") |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | @implementer(RIDummy) |
|---|
| 109 | @attr.s(frozen=True) |
|---|
| 110 | class DummyStorageServer(object): # type: ignore # warner/foolscap#78 |
|---|
| 111 | get_anonymous_storage_server = attr.ib() |
|---|
| 112 | |
|---|
| 113 | def remote_just_some_method(self): |
|---|
| 114 | pass |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | @implementer(IStorageServer) |
|---|
| 118 | @attr.s |
|---|
| 119 | class DummyStorageClient(object): # type: ignore # incomplete implementation |
|---|
| 120 | get_rref = attr.ib() |
|---|
| 121 | configuration = attr.ib() |
|---|
| 122 | announcement = attr.ib() |
|---|