Skip to content

Commit cff61fd

Browse files
Merge pull request #41 from matrix-org/containerization
Containerize the SFU and publish images to the registry
2 parents f1231bf + 0d0f567 commit cff61fd

File tree

7 files changed

+215
-30
lines changed

7 files changed

+215
-30
lines changed

.github/workflows/docker.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Build and publish Docker image
2+
3+
on:
4+
push:
5+
branches:
6+
- 'main'
7+
pull_request: {}
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
IMAGE_NAME: ${{ github.repository }}
12+
13+
jobs:
14+
build-and-push-image:
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: read
18+
packages: write
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v3
23+
24+
- name: Log in to the Container registry
25+
uses: docker/login-action@v2
26+
with:
27+
registry: ${{ env.REGISTRY }}
28+
username: ${{ github.actor }}
29+
password: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Extract metadata (tags, labels) for Docker
32+
id: meta
33+
uses: docker/metadata-action@v4
34+
with:
35+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36+
tags: |
37+
type=sha,format=short,event=branch
38+
type=ref,event=pr
39+
type=semver,pattern={{version}}
40+
41+
- name: Build and push Docker image
42+
uses: docker/build-push-action@v3
43+
with:
44+
context: .
45+
push: true
46+
tags: ${{ steps.meta.outputs.tags }}
47+
labels: ${{ steps.meta.outputs.labels }}

Dockerfile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
##
2+
## Build
3+
##
4+
FROM golang:1.19 AS build
5+
6+
WORKDIR /app
7+
8+
COPY go.mod ./
9+
COPY go.sum ./
10+
11+
# Cache dependencies before building and copying the source code, so that
12+
# we don't need to re-download when building and so that the change in the
13+
# source code do not invalidate our downloaded layer.
14+
RUN go mod download
15+
16+
COPY ./src ./src
17+
18+
RUN go build -o /waterfall ./src
19+
20+
21+
##
22+
## Deploy
23+
##
24+
FROM ubuntu:22.04
25+
26+
RUN apt update \
27+
&& apt install -y --no-install-recommends \
28+
dumb-init \
29+
&& rm -rf /var/lib/apt/lists/*
30+
31+
WORKDIR /
32+
33+
COPY --from=build /waterfall /usr/bin/waterfall
34+
35+
ENTRYPOINT ["dumb-init", "--"]
36+
CMD ["waterfall"]

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,37 @@ from the MSC.
4444
* `./scripts/build.sh`
4545
* `./dist/bin`
4646
* Access at <http://localhost:8080>
47+
48+
## Docker
49+
50+
### Building
51+
52+
In order to build a docker image, run the following in the root directory:
53+
54+
`$ docker build . -t matrix/materwall`
55+
56+
### Running
57+
58+
#### Easy Way
59+
60+
Just use `docker-compose up` and you're good to go.
61+
62+
Or `docker-compose up -d` if you're running it on a server in a detached state.
63+
64+
You can then find your container ID by checking `docker container ls`.
65+
66+
Which could then be used to e.g. check the container logs with `docker container logs <ID>`.
67+
68+
If you're developing locally, you can replace the path to the image with your own image tag, e.g. `matrix/waterfall`.
69+
70+
#### Hard Way
71+
72+
To run the image from the current directory assuming that there is a `config.yaml`:
73+
74+
```
75+
$ docker run \
76+
-v $(pwd)/config.yaml:/config.yaml \
77+
--network host \
78+
-it --rm matrix/waterfall \
79+
sfu --config config.yaml
80+
```

docker-compose.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: "3.3"
2+
services:
3+
waterfall:
4+
container_name: waterfall
5+
image: ghcr.io/matrix-org/waterfall:main
6+
network_mode: host
7+
restart: always
8+
environment:
9+
CONFIG: |
10+
homeserverurl: "http://localhost:8008"
11+
userid: "@sfu:shadowfax"
12+
accesstoken: "..."
13+
timeout: 30

src/call.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ func (c *Call) SendDataChannelMessage(msg event.SFUMessage) {
513513
}
514514

515515
func (c *Call) CheckKeepAliveTimestamp() {
516-
timeout := time.Second * time.Duration(config.Timeout)
516+
timeout := time.Second * time.Duration(config.KeepAliveTimeout)
517517
for range time.Tick(timeout) {
518518
if c.lastKeepAliveTimestamp.Add(timeout).Before(time.Now()) {
519519
if c.PeerConnection.ConnectionState() != webrtc.PeerConnectionStateClosed {

src/config.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"github.com/sirupsen/logrus"
9+
"gopkg.in/yaml.v3"
10+
"maunium.net/go/mautrix/id"
11+
)
12+
13+
// The mandatory SFU configuration.
14+
type Config struct {
15+
// The Matrix ID (MXID) of the SFU.
16+
UserID id.UserID
17+
// The ULR of the homeserver that SFU talks to.
18+
HomeserverURL string
19+
// The access token for the Matrix SDK.
20+
AccessToken string
21+
// Keep-alive timeout for WebRTC connections. If no keep-alive has been received
22+
// from the client for this duration, the connection is considered dead.
23+
KeepAliveTimeout int
24+
}
25+
26+
// Tries to load a config from the `CONFIG` environment variable.
27+
// If the environment variable is not set, tries to load a config from the
28+
// provided path to the config file (YAML). Returns an error if the config could
29+
// not be loaded.
30+
func loadConfig(path string) (*Config, error) {
31+
config, err := loadConfigFromEnv()
32+
if err != nil {
33+
if !errors.Is(err, ErrNoConfigEnvVar) {
34+
return nil, err
35+
}
36+
37+
return loadConfigFromPath(path)
38+
}
39+
40+
return config, nil
41+
}
42+
43+
// ErrNoConfigEnvVar is returned when the CONFIG environment variable is not set.
44+
var ErrNoConfigEnvVar = errors.New("environment variable not set or invalid")
45+
46+
// Tries to load the config from environment variable (`CONFIG`).
47+
// Returns an error if not all environment variables are set.
48+
func loadConfigFromEnv() (*Config, error) {
49+
configEnv := os.Getenv("CONFIG")
50+
if configEnv == "" {
51+
return nil, ErrNoConfigEnvVar
52+
}
53+
54+
return loadConfigFromString(configEnv)
55+
}
56+
57+
// Tries to load a config from the provided path.
58+
func loadConfigFromPath(path string) (*Config, error) {
59+
logrus.WithField("path", path).Info("loading config")
60+
61+
file, err := os.ReadFile(path)
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to read file: %w", err)
64+
}
65+
66+
return loadConfigFromString(string(file))
67+
}
68+
69+
// Load config from the provided string.
70+
// Returns an error if the string is not a valid YAML.
71+
func loadConfigFromString(configString string) (*Config, error) {
72+
logrus.Info("loading config from string")
73+
74+
var config Config
75+
if err := yaml.Unmarshal([]byte(configString), &config); err != nil {
76+
return nil, fmt.Errorf("failed to unmarshal YAML file: %w", err)
77+
}
78+
79+
return &config, nil
80+
}

src/main.go

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,15 @@ package main
1818

1919
import (
2020
"flag"
21-
"fmt"
22-
"io/ioutil"
2321
"os"
2422
"os/signal"
2523
"runtime"
2624
"runtime/pprof"
2725
"syscall"
2826

2927
"github.com/sirupsen/logrus"
30-
yaml "gopkg.in/yaml.v3"
31-
"maunium.net/go/mautrix/id"
3228
)
3329

34-
type Config struct {
35-
UserID id.UserID
36-
HomeserverURL string
37-
AccessToken string
38-
Timeout int
39-
}
40-
4130
var config *Config
4231

4332
var logTime = flag.Bool("logTime", false, "whether or not to print time and date in logs")
@@ -95,22 +84,6 @@ func initLogging(logTime *bool) {
9584
logrus.SetFormatter(formatter)
9685
}
9786

98-
func loadConfig(configFilePath string) (*Config, error) {
99-
logrus.WithField("path", configFilePath).Info("loading config")
100-
101-
file, err := ioutil.ReadFile(configFilePath)
102-
if err != nil {
103-
return nil, fmt.Errorf("failed to read file: %w", err)
104-
}
105-
var config Config
106-
107-
if err := yaml.Unmarshal(file, &config); err != nil {
108-
return nil, fmt.Errorf("failed to unmarshal YAML file: %w", err)
109-
}
110-
111-
return &config, nil
112-
}
113-
11487
func killListener(c chan os.Signal, beforeExit []func()) {
11588
<-c
11689

@@ -143,8 +116,10 @@ func main() {
143116
go killListener(c, beforeExit)
144117

145118
var err error
146-
if config, err = loadConfig(*configFilePath); err != nil {
147-
logrus.WithError(err).Fatal("failed to load config file")
119+
config, err = loadConfig(*configFilePath)
120+
121+
if err != nil {
122+
logrus.WithError(err).Fatal("could not load config")
148123
}
149124

150125
InitMatrix()

0 commit comments

Comments
 (0)