You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add deployment.GetFullyQualifiedHomeserverName(t, hsName) to support custom Deployment (matrix-org#780)
*Split off from matrix-org#778 per
[discussion](matrix-org#778 (comment)
Spawning from a real use-case with a custom `Deployment`/`Deployer` (Element-internal).
Introduce `complement.Deployment.GetFullyQualifiedHomeserverName(hsName)` to allow the
per-deployment short homeserver aliases like `hs1` to be mapped to something else that's
resolvable in your custom deployments context. Example: `hs1` -> `hs1.shard1:8481`.
This is useful for situations like the following where you have to specify the via
servers in a federated join request during a test:
```go
alice.MustJoinRoom(t, bobRoomID, []string{
deployment.GetFullyQualifiedHomeserverName(t, "hs2"),
})
```
### But why does this have to be part of the `complement.Deployment` interface instead of your own custom deployment?
- Tests only have access to the generic `complement.Deployment` interface
- We can't derive fully-qualified homeserver names from the existing
`complement.Deployment` interface
- While we could cheekily cast the generic `complement.Deployment` back to
`CustomDeployment` in our own tests (and have the helper in the `CustomDeployment`
instead), if we start using something exotic in our out-of-repo Complement tests, the
suite of existing Complement tests in this repo will not be compatible.
(also see below)
### Motivating custom `Deployment` use case
[`complement.Deployment`](https://github.com/matrix-org/complement/blob/d2e04c995666fbeb0948e6a4ed52d3fbb45fbdf7/test_package.go#L21-L69)
is an interface that can be backed by anything. For reference, custom deployments were
introduced in matrix-org#750. The [default
`Deployment` naming scheme in Complement is `hs1`, `hs2`,
etc](https://github.com/matrix-org/complement/blob/6b63eff50804beb334ca215650f5027ddf02ae9a/test_package.go#L198).
It's really nice and convenient to be able to simply refer to homeservers as `hs1`, etc
within a deployment. And using consistent names like this makes tests compatible with
each other regardless of which `Deployment` is being used.
The built-in `Deployment` in Complement has each homeserver in a Docker container which
already has network aliases like `hs1`, `hs2`, etc so no translation is needed from
friendly name to resolvable address. When one homeserver needs to federate with the
other, it can simply make a request to `https://hs1:8448/...` per [spec on resolving
server names](https://spec.matrix.org/v1.13/server-server-api/#resolving-server-names).
Right-now, we hard-code `hs1` across the tests when we specify ["via" servers in join
requests](https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv3joinroomidoralias)
but that only works if you follow the strict single-deployment naming scheme.
https://github.com/matrix-org/complement/blob/6b63eff50804beb334ca215650f5027ddf02ae9a/tests/federation_rooms_invite_test.go#L112
In the current setup of our custom `Deployment`, each `Deployment` is a "shard"
application that can deploy multiple homeserver "tenants". We specifically want to test
that homeservers between multiple shards can federate with each other as a sanity check
(make sure our shards can deploy homeserver tenants correctly). If we keep using the
consistent `hs1`, `hs2` naming scheme for each `Deployment` we're going to have
conflicts. This is where `deployment.GetFullyQualifiedHomeserverName(t, hsName)` comes
in handy. We can call `deployment1.GetFullyQualifiedHomeserverName(t, "hs1")` ->
`hs1.shard1` and also `deployment2.GetFullyQualifiedHomeserverName(t, "hs1")` ->
`hs1.shard2` to get their unique resolvable addresses in the network.
Additionally, the helper removes the constraint of needing the network to strictly
resolve `hs1`, `hs2` hostnames to their respective homeservers. Whenever you need to
refer to another homeserver, use `deployment.GetFullyQualifiedHomeserverName(hsName)` to
take care of the nuance of environment that the given `Deployment` creates.
Example of a cross-deployment test:
```go
func TestMain(m *testing.M) {
complement.TestMain(m, "custom_tests",
complement.WithDeployment(internal.MakeCustomDeployment()),
)
}
func TestCrossShardFederation(t *testing.T) {
// Create two shards with their own homeserver tenants
shardDeployment1 := complement.Deploy(t, 1)
defer shardDeployment1.Destroy(t)
shardDeployment2 := complement.Deploy(t, 1)
defer shardDeployment2.Destroy(t)
alice := shardDeployment1.Register(t, "hs1", helpers.RegistrationOpts{})
bob := shardDeployment2.Register(t, "hs1", helpers.RegistrationOpts{})
aliceRoomID := alice.MustCreateRoom(t, map[string]any{
"preset": "public_chat",
})
bobRoomID := bob.MustCreateRoom(t, map[string]any{
"preset": "public_chat",
})
t.Run("parallel", func(t *testing.T) {
t.Run("shard1 -> shard2", func(t *testing.T) {
// Since these tests use the same config, they can be run in parallel
t.Parallel()
alice.MustJoinRoom(t, bobRoomID, []string{
shardDeployment2.GetFullyQualifiedHomeserverName(t, "hs1"),
})
bob.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(alice.UserID, bobRoomID))
})
t.Run("shard2 -> shard1", func(t *testing.T) {
// Since these tests use the same config, they can be run in parallel
t.Parallel()
bob.MustJoinRoom(t, aliceRoomID, []string{
shardDeployment1.GetFullyQualifiedHomeserverName(t, "hs1"),
})
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(bob.UserID, aliceRoomID))
})
})
}
```
Per the discussion in
matrix-org#780 (comment),
multiple-deployments per test doesn't work with Complement's `Deployment` implementation
yet and the `Deployment` is meant to encapsulate an _entire_ deployment, all servers and
network links between them. This was the motivating use case but use at your own
discretion until further guidance is given.
// It is not supported to call ServerName() before Listen() because Listen() modifies the server name.
143
144
// Listen() will select a random OS-provided high-numbered port to listen on, which then needs to be
144
145
// retrofitted into the server name so containers know how to route to it.
145
-
func (s*Server) ServerName() string {
146
+
func (s*Server) ServerName() spec.ServerName {
146
147
if!s.listening {
147
148
ct.Fatalf(s.t, "ServerName() called before Listen() - this is not supported because Listen() chooses a high-numbered port and thus changes the server name. Ensure you Listen() first!")
148
149
}
@@ -205,28 +206,31 @@ func (s *Server) FederationClient(deployment FederationDeployment) fclient.Feder
205
206
ct.Fatalf(s.t, "FederationClient() called before Listen() - this is not supported because Listen() chooses a high-numbered port and thus changes the server name and thus changes the way federation requests are signed. Ensure you Listen() first!")
206
207
}
207
208
identity:= fclient.SigningIdentity{
208
-
ServerName: spec.ServerName(s.ServerName()),
209
+
ServerName: s.ServerName(),
209
210
KeyID: s.KeyID,
210
211
PrivateKey: s.Priv,
211
212
}
212
-
f:=fclient.NewFederationClient(
213
+
fedClient:=fclient.NewFederationClient(
213
214
[]*fclient.SigningIdentity{&identity},
214
215
fclient.WithTransport(deployment.RoundTripper()),
215
216
)
216
-
returnf
217
+
returnfedClient
217
218
}
218
219
219
220
// MustSendTransaction sends the given PDUs/EDUs to the target destination, returning an error if the /send fails or if the response contains an error
0 commit comments