Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions 03_container_layer/docker/admin/mattermost/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Prevent secrets and editor artifacts from leaking into the build context.
.env
*.env
*.key
*.pem
README.md
24 changes: 24 additions & 0 deletions 03_container_layer/docker/admin/mattermost/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# ISSUE 143
#
# Copy to .env and fill in values before running: cp .env.example .env
# .env is gitignored — never commit real secrets.
#

# ── Mattermost ─────────────────────────────────────────────────────────────
MM_BASE_URL=http://localhost:8065

# ── Initial admin (must match admins[0] in provisioning/users.yml) ─────────
MM_ADMIN_USER=mm-admin
MM_ADMIN_PASS=Admin1234!

# ── Default team ────────────────────────────────────────────────────────────
MM_TEAM_NAME=range42

# ── PostgreSQL ──────────────────────────────────────────────────────────────
POSTGRES_USER=mattermost
POSTGRES_PASSWORD=CHANGEME_replace_before_deploying
POSTGRES_DB=mattermost

# ── Host ports ──────────────────────────────────────────────────────────────
HTTP_PORT=8065
32 changes: 32 additions & 0 deletions 03_container_layer/docker/admin/mattermost/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# ISSUE 143
#

#
# BUILDER — golang:alpine (Mattermost is Go-based)
# Installs yq for YAML parsing; copies and pre-validates provisioning scripts.
#
FROM golang:alpine AS builder

RUN apk add --no-cache jq && \
wget -q https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64 \
-O /usr/bin/yq && chmod +x /usr/bin/yq

COPY provisioning/ /provisioning/
RUN chmod +x /provisioning/init.sh

#
# RUNTIME — mattermost/mattermost-team-edition:latest (Alpine-based, ships mattermost binary)
# Copies tooling and provisioning scripts from builder.
# Acts as the provisioner sidecar: creates users via CLI + REST API for tokens.
#
FROM mattermost/mattermost-team-edition:latest AS runtime

COPY --from=builder /usr/bin/yq /usr/bin/yq
COPY --from=builder /usr/bin/jq /usr/bin/jq
COPY --from=builder /provisioning/ /provisioning/

USER root
RUN chmod +x /provisioning/init.sh

ENTRYPOINT ["/provisioning/init.sh"]
92 changes: 92 additions & 0 deletions 03_container_layer/docker/admin/mattermost/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#
# ISSUE 143
#

SERVICE = mattermost
PROVISIONER = mattermost-provisioner
DEBUG_SERVICE = $(SERVICE)-debug

.PHONY: help up down stop build rebuild build-up rebuild-up reprovision logs-provisioner tokens term term-debug-build clean print

help:
@echo ""
@echo ""
@echo " Available : "
@echo ""
@echo " make up - run the full stack (db + mattermost + provisioner) in background"
@echo " make down - stop and remove all containers"
@echo " make stop - stop $(SERVICE) only"
@echo ""
@echo " make build - build the provisioner image"
@echo " make build-up - build provisioner image then start the full stack"
@echo ""
@echo " make rebuild - full rebuild without cache"
@echo " make rebuild-up - full rebuild without cache then start"
@echo ""
@echo " make reprovision - remove the provisioning stamp and restart provisioner"
@echo " make logs-provisioner - tail provisioner output"
@echo " make tokens - print generated personal access tokens"
@echo ""
@echo " make term - open shell in $(SERVICE) container"
@echo " make term-debug-build - build and open shell in $(DEBUG_SERVICE)"
@echo " make clean - delete all containers, images, volumes and unused network"
@echo " make print - print this help"
@echo ""
@echo ""

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

up:
docker compose up -d
down:
docker compose down
stop:
docker compose stop $(SERVICE)

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

build:
docker compose build

rebuild:
docker compose down
docker compose build --no-cache

# # # # # # # # # # # #

build-up:
docker compose build
docker compose up -d

rebuild-up:
docker compose down
docker compose build --no-cache
docker compose up -d

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

reprovision:
docker run --rm -v mattermost-tokens:/tokens alpine rm -f /tokens/.provisioned
docker compose up $(PROVISIONER)

logs-provisioner:
docker compose logs -f $(PROVISIONER)

tokens:
docker run --rm -v mattermost-tokens:/tokens alpine cat /tokens/tokens.txt

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

clean:
docker compose down -v --rmi all

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

term:
docker exec -it $(SERVICE) /bin/sh

term-debug-build:
docker compose up --build -d debug
docker exec -it $(DEBUG_SERVICE) /bin/sh

print: help
150 changes: 150 additions & 0 deletions 03_container_layer/docker/admin/mattermost/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Mattermost — Standalone Docker Deployment

Issue: [#143](https://github.com/range42/range42-catalog/issues/143)

Standalone Mattermost Team Edition instance with automated user provisioning
and personal access token generation.
All accounts are declared in `provisioning/users.yml`; registration is disabled
via environment variables by default.

---

## Prerequisites

- Docker 24+ with Compose v2
- `make`

---

## Quick Start

```bash
cp .env.example .env # edit secrets before deploying
make build-up # build provisioner image, start full stack
make logs-provisioner # watch bootstrap output
```

Mattermost will be available at `http://localhost:8065` (or `MM_BASE_URL`).

---

## Build & Push

```bash
# Build only the provisioner image
make build

# Full rebuild (no cache)
make rebuild

# Push to a registry (replace tag as needed)
docker tag mattermost-provisioner registry.example.com/range42/mattermost-provisioner:latest
docker push registry.example.com/range42/mattermost-provisioner:latest
```

---

## Declaring Users

Edit `provisioning/users.yml` before the first `make up`:

```yaml
admins:
- username: mm-admin
email: admin@range42.local
password: "Admin1234!"

users:
- username: trainee01
email: trainee01@range42.local
password: "Trainee1234!"
```

- Add/remove entries to change the provisioned user set.
- `admins[]` entries receive Mattermost system-admin privileges.
- `users[]` entries are regular accounts.

**The provisioner runs only once** (guarded by `/tokens/.provisioned`).
To re-provision after changes, run:

```bash
make reprovision
```

---

## Token Retrieval

Personal access tokens are generated for every user at provisioning time
and written to `/tokens/tokens.txt` (one `username:token` per line).

```bash
# Via make target
make tokens

# Via docker exec
docker exec mattermost-provisioner cat /tokens/tokens.txt
```

---

## API Usage Examples

```bash
# Replace <token> with a value from tokens.txt

# Get current user info
curl -H "Authorization: Bearer <token>" http://localhost:8065/api/v4/users/me

# List channels in the default team
curl -H "Authorization: Bearer <token>" \
"http://localhost:8065/api/v4/users/me/teams/channels"

# Post a message to a channel
curl -X POST http://localhost:8065/api/v4/posts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"channel_id":"<channel_id>","message":"Hello from the API"}'
```

---

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `MM_BASE_URL` | `http://localhost:8065` | Public URL of the Mattermost instance |
| `MM_ADMIN_USER` | `mm-admin` | Must match `admins[0].username` in `users.yml` |
| `MM_ADMIN_PASS` | `Admin1234!` | Must match `admins[0].password` in `users.yml` |
| `MM_TEAM_NAME` | `range42` | Default team created by provisioner |
| `POSTGRES_USER` | `mattermost` | DB user |
| `POSTGRES_PASSWORD` | *(required)* | DB password |
| `POSTGRES_DB` | `mattermost` | DB name |
| `HTTP_PORT` | `8065` | Host port for Mattermost HTTP |

---

## Troubleshooting

**Provisioner exits immediately with "Already provisioned"**
Remove the stamp and re-run: `make reprovision`

**`mattermost user create` fails silently**
Check provisioner logs: `make logs-provisioner`
The stamp is NOT written on failure — restart the provisioner to retry.

**Token creation returns empty**
Ensure `MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS=true` is set (already the
default in `compose.yml`). Verify with:
```bash
curl http://localhost:8065/api/v4/config/client?format=old | jq '.EnableUserAccessTokens'
```

**Port 8065 already in use**
Set `HTTP_PORT=8066` (or any free port) in `.env`.

**Mattermost fails to start / DB connection refused**
Check that the `db` service passed its healthcheck before `mattermost` started:
```bash
docker compose logs db
```
90 changes: 90 additions & 0 deletions 03_container_layer/docker/admin/mattermost/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#
# ISSUE 143
#

services:

db:
image: postgres:16-alpine
container_name: mattermost-db
environment:
POSTGRES_USER: ${POSTGRES_USER:-mattermost}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mattermost}
POSTGRES_DB: ${POSTGRES_DB:-mattermost}
volumes:
- mattermost-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mattermost}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped

mattermost: &main
image: mattermost/mattermost-team-edition:latest
container_name: mattermost
environment:
MM_SQLSETTINGS_DRIVERNAME: postgres
MM_SQLSETTINGS_DATASOURCE: "postgres://${POSTGRES_USER:-mattermost}:${POSTGRES_PASSWORD:-mattermost}@db:5432/${POSTGRES_DB:-mattermost}?sslmode=disable&connect_timeout=10"
MM_SERVICESETTINGS_SITEURL: ${MM_BASE_URL:-http://localhost:8065}
MM_SERVICESETTINGS_ENABLEUSERSTATUSESFORPOSTS: "false"
MM_SERVICESETTINGS_ENABLEAPITEAMCREATION: "true"
MM_SERVICESETTINGS_ENABLEAPICREATEACCOUNT: "true"
MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS: "true"
MM_TEAMSETTINGS_MAXUSERSPERTEAM: "200"
MM_TEAMSETTINGS_ENABLEOPENSERVER: "true"
MM_EMAILSETTINGS_SENDEMAILNOTIFICATIONS: "false"
MM_EMAILSETTINGS_REQUIREEMAILVERIFICATION: "false"
MM_LOGSETTINGS_CONSOLELEVEL: "ERROR"
MM_LOGSETTINGS_ENABLEFILE: "false"
volumes:
- mattermost-data:/mattermost/data
- mattermost-logs:/mattermost/logs
- mattermost-config:/mattermost/config
- mattermost-plugins:/mattermost/plugins
ports:
- "${HTTP_PORT:-8065}:8065"
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8065/api/v4/system/ping"]
interval: 15s
timeout: 10s
retries: 15
start_period: 60s
restart: unless-stopped
#
# #### #### do not remove.
#
# debug:
# <<: *main
# container_name: mattermost-debug
# command: ["sleep", "infinity"]

provisioner:
build:
context: .
dockerfile: Dockerfile
container_name: mattermost-provisioner
environment:
MM_URL: http://mattermost:8065
MM_ADMIN_USER: ${MM_ADMIN_USER:-mm-admin}
MM_ADMIN_PASS: ${MM_ADMIN_PASS:-Admin1234!}
MM_TEAM_NAME: ${MM_TEAM_NAME:-range42}
USERS_FILE: /provisioning/users.yml
volumes:
- mattermost-config:/mattermost/config
- mattermost-tokens:/tokens
depends_on:
mattermost:
condition: service_healthy
restart: "no"

volumes:
mattermost-db-data:
mattermost-data:
mattermost-logs:
mattermost-config:
mattermost-plugins:
mattermost-tokens:
Loading