Skip to content

Commit f11dfc5

Browse files
authored
Initialize project with Docker and Flask server configuration
1 parent 8490d9a commit f11dfc5

14 files changed

+538
-92
lines changed

.dockerignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# .dockerignore
2+
.git
3+
.env
4+
.vscode
5+
__pycache__
6+
*.pyc
7+
.pytest_cache

.env.example

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Server Configuration
2+
SV_PORT_HOST=5000
3+
SV_WORKERS=2
4+
5+
# Security
6+
ENABLE_CORS=false
7+
ALLOWED_ORIGINS=*
8+
9+
# Options
10+
CATALOG_FILE=catalog.rdf

.github/workflows/docker-build.yml

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Docker Build and Push
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
tags: [ 'v*.*.*' ]
7+
pull_request:
8+
branches: [ main ]
9+
10+
env:
11+
REGISTRY: ghcr.io
12+
IMAGE_NAME: ${{ github.repository }}
13+
DOCKERFILE_PATH: docker/Dockerfile
14+
15+
jobs:
16+
build:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: read
20+
packages: write
21+
security-events: write
22+
23+
steps:
24+
- name: Checkout repository
25+
uses: actions/checkout@v4
26+
27+
- name: Set up QEMU
28+
uses: docker/setup-qemu-action@v3
29+
30+
- name: Set up Docker Buildx
31+
uses: docker/setup-buildx-action@v3
32+
33+
- name: Login to GitHub Container Registry
34+
if: github.event_name != 'pull_request'
35+
uses: docker/login-action@v3
36+
with:
37+
registry: ${{ env.REGISTRY }}
38+
username: ${{ github.actor }}
39+
password: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Extract metadata for Docker
42+
id: meta
43+
uses: docker/metadata-action@v5
44+
with:
45+
images: ${{ env.REGISTRY }}/${{ github.repository }}
46+
tags: |
47+
type=ref,event=branch
48+
type=ref,event=pr
49+
type=semver,pattern={{version}}
50+
type=semver,pattern={{major}}.{{minor}}
51+
labels: |
52+
org.opencontainers.image.title=Easy RDF Endpoint
53+
org.opencontainers.image.description=A simple RDF endpoint server
54+
org.opencontainers.image.version={{version}}
55+
56+
- name: Build and push Docker image
57+
uses: docker/build-push-action@v5
58+
with:
59+
context: .
60+
file: ${{ env.DOCKERFILE_PATH }}
61+
push: ${{ github.event_name != 'pull_request' }}
62+
tags: ${{ steps.meta.outputs.tags }}
63+
labels: ${{ steps.meta.outputs.labels }}
64+
65+
- name: Run Hadolint
66+
uses: hadolint/hadolint-action@v3.1.0
67+
with:
68+
dockerfile: ${{ env.DOCKERFILE_PATH }}
69+
no-fail: true
70+
71+
- name: Run Trivy vulnerability scanner
72+
uses: aquasecurity/trivy-action@master
73+
with:
74+
image-ref: ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.sha }}
75+
format: 'sarif'
76+
output: 'trivy-results.sarif'
77+
78+
- name: Upload Trivy scan results
79+
uses: github/codeql-action/upload-sarif@v3
80+
if: always()
81+
with:
82+
sarif_file: 'trivy-results.sarif'

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.env
2+
.vscode

README.md

+37-92
Original file line numberDiff line numberDiff line change
@@ -3,117 +3,62 @@
33
## Overview
44
This project provides a simple **Dockerized RDF endpoint** that serves an `.rdf` file with the correct `Content-Type: application/rdf+xml`, ensuring compatibility with **DCAT-AP federators** and **RDF parsers** like `rdflib`.
55

6-
## Quick Start
7-
### Run with Docker
8-
```sh
9-
docker build -t rdf-server .
10-
docker run -p 5000:5000 -v $(pwd)/catalog.rdf:/app/catalog.rdf rdf-server
11-
```
126

13-
Then, access your RDF file at:
7+
## Quick Start
8+
Use [Codespaces](https://github.com/features/codespaces) to test `easy-rdf-endpoint` in your browser
9+
<center><a href='https://codespaces.new/mjanez/easy-rdf-endpoint'><img src='https://github.com/codespaces/badge.svg' alt='GitHub Codespaces' style={{maxWidth: '100%'}}/></a></center>
1410

15-
👉 `http://localhost:5000/catalog.rdf`
11+
## Getting started
12+
**Requirements**:
13+
* [Docker](https://docs.docker.com/get-docker/)
1614

17-
### Run with Docker Compose (Optional)
18-
If you prefer `docker-compose`, use:
15+
### Clone and configure
16+
Before starting the deployment, you'll need to set up a `.env` file. This file is crucial as it contains environment variables that the application needs to run properly.
1917

20-
```sh
21-
docker-compose up -d
22-
```
23-
---
18+
1. Clone project
19+
```shell
20+
cd /path/to/my/project
21+
git clone https://github.com/mjanez/easy-rdf-endpoint.git & cd ckan-docker
22+
```
2423

25-
## **🛠 Project Structure**
26-
```
27-
/rdf-server
28-
│── catalog.rdf # Your RDF file (replace with your own)
29-
│── app.py # Flask server to serve RDF
30-
│── Dockerfile # Docker setup
31-
│── requirements.txt # Python dependencies
32-
│── docker-compose.yml # Optional: Docker Compose config
33-
│── README.md # Documentation
34-
```
35-
36-
---
37-
38-
## Code Explanation
24+
2. Copy the `.env.example` template and modify the resulting `.env` to suit your needs.
3925

40-
### Flask App (`app.py`)
41-
A minimal Flask server to serve `catalog.rdf` with the correct `Content-Type`.
26+
```shell
27+
cp .env.example .env
28+
```
4229

43-
```python
44-
from flask import Flask, send_file
30+
Adjust the vars as necessary
31+
```ini
32+
# Host Ports
33+
SV_PORT_HOST=5000
4534
46-
app = Flask(__name__)
35+
# Options
36+
CATALOG_FILE=catalog.rdf
37+
```
4738

48-
@app.route("/catalog.rdf")
49-
def serve_rdf():
50-
return send_file("catalog.rdf", mimetype="application/rdf+xml")
39+
3. Build & up the container.
5140

52-
if __name__ == "__main__":
53-
app.run(host="0.0.0.0", port=5000)
41+
```sh
42+
docker compose up -d
5443
```
5544

56-
---
57-
58-
### Dockerfile
59-
This Dockerfile creates a lightweight Flask container.
60-
61-
```dockerfile
62-
# Use lightweight Python image
63-
FROM python:3.9-slim
64-
65-
# Set working directory
66-
WORKDIR /app
67-
68-
# Copy required files
69-
COPY app.py requirements.txt catalog.rdf .
70-
71-
# Install dependencies
72-
RUN pip install -r requirements.txt
73-
74-
# Expose port
75-
EXPOSE 5000
45+
Then, access your RDF file at:
7646

77-
# Run Flask server
78-
CMD ["python", "app.py"]
79-
```
47+
👉 `http://localhost:5000/catalog.rdf`
8048

8149
---
8250

83-
### Python Dependencies (`requirements.txt`)
84-
```txt
85-
flask
51+
## Project Structure
8652
```
87-
88-
---
89-
90-
### Docker Compose (Optional)
91-
For easier deployment using `docker-compose`.
92-
93-
```yaml
94-
version: '3.8'
95-
services:
96-
rdf-server:
97-
build: .
98-
ports:
99-
- "5000:5000"
100-
volumes:
101-
- ./catalog.rdf:/app/catalog.rdf
53+
/rdf-server
54+
│── /data/catalog.rdf # Your RDF file (replace with your own)
55+
│── /scr/app.py # Flask server to serve RDF
56+
│── /src/requirements.txt # Python dependencies
57+
│── /docker/Dockerfile # Docker setup
58+
│── docker-compose.yml # Docker Compose config
59+
│── README.md # Documentation
10260
```
10361
104-
---
105-
106-
## Deploy in Codespaces
107-
1. Open **GitHub Codespaces**.
108-
2. Clone this repository.
109-
3. Run:
110-
```sh
111-
docker-compose up -d
112-
```
113-
4. Access: `http://localhost:5000/catalog.rdf`
114-
115-
---
116-
11762
## Notes
11863
- Replace `catalog.rdf` with your actual RDF file.
11964
- Modify `app.py` if you need additional routes or enhancements.

data/catalog.rdf

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
3+
xmlns:dcat="http://www.w3.org/ns/dcat#"
4+
xmlns:dct="http://purl.org/dc/terms/"
5+
xmlns:foaf="http://xmlns.com/foaf/0.1/"
6+
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
7+
xmlns:odrs="http://schema.theodi.org/odrs#">
8+
9+
<dcat:Catalog rdf:about="http://dcat-ap-es.ejemplo.org/catalogo">
10+
<dct:title xml:lang="es">Catálogo de Datos Abiertos</dct:title>
11+
<dct:title xml:lang="en">Open Data Catalog</dct:title>
12+
<dct:description xml:lang="es">Catálogo de Datos Abiertos que sirve de ejemplo en DCAT-AP-ES.</dct:description>
13+
<dct:description xml:lang="en">Open Data Catalogue that serves as an example in DCAT-AP-ES.</dct:description>
14+
<foaf:homepage rdf:resource="http://dcat-ap-es.ejemplo.org"/>
15+
<dct:publisher rdf:resource="http://datos.gob.es/recurso/sector-publico/org/Organismo/Identificador_Organismo"/>
16+
<dcat:themeTaxonomy rdf:resource="http://datos.gob.es/kos/sector-publico/sector"/>
17+
<dct:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2025-01-01T09:00:00+01:00</dct:issued>
18+
<dct:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">2025-01-02T12:00:00+01:00</dct:modified>
19+
<dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/SPA"/>
20+
<dct:language rdf:resource="http://publications.europa.eu/resource/authority/language/ENG"/>
21+
<dct:spatial rdf:resource="http://datos.gob.es/recurso/sector-publico/territorio/Pais/España"/>
22+
<dct:creator>
23+
<foaf:Organization>
24+
<foaf:name>Nombre del departamento del organismo</foaf:name>
25+
</foaf:Organization>
26+
</dct:creator>
27+
28+
<dct:rights>
29+
<rdf:Description rdf:about="http://dcat-ap-es.ejemplo.org/catalogo/derechos-de-acceso">
30+
<rdf:type rdf:resource="http://schema.theodi.org/odrs#RightsStatement"/>
31+
<rdfs:label>Derechos relativos a la reutilización del Catálogo de Datos Abiertos</rdfs:label>
32+
<odrs:dataLicense rdf:resource="http://publications.europa.eu/resource/authority/licence/CC_BY_4_0"/>
33+
<odrs:attributionText>Atribución de autoría al Organismo</odrs:attributionText>
34+
<odrs:attributionURL rdf:resource="http://datos.gob.es/recurso/sector-publico/org/Organismo/Identificador_Organismo"/>
35+
</rdf:Description>
36+
</dct:rights>
37+
38+
<dct:license rdf:resource="http://publications.europa.eu/resource/authority/licence/CC_BY_4_0"/>
39+
<dct:hasPart>
40+
<dcat:Catalog rdf:about="http://dcat-ap-es.ejemplo.org/catalogo-vinculado">
41+
<dct:title xml:lang="es">Catálogo vinculado</dct:title>
42+
<dct:description xml:lang="es">Catálogo asociado que sirve de ejemplo en DCAT-AP-ES.</dct:description>
43+
<dct:description xml:lang="en">Associated Open Data Catalogue serving as an example in DCAT-AP-ES.</dct:description>
44+
<dct:publisher rdf:resource="http://datos.gob.es/recurso/sector-publico/org/Organismo/Identificador_Organismo"/>
45+
</dcat:Catalog>
46+
</dct:hasPart>
47+
48+
<dcat:dataset>
49+
<dcat:Dataset rdf:about="http://dcat-ap-es.ejemplo.org/dataset/dataset-ejemplo-1">
50+
<dct:title xml:lang="es">Dataset de ejemplo</dct:title>
51+
<dct:title xml:lang="en">Example Dataset</dct:title>
52+
<dct:description xml:lang="es">Descripción de un dataset de ejemplo.</dct:description>
53+
<dct:description xml:lang="en">Example dataset description.</dct:description>
54+
</dcat:Dataset>
55+
</dcat:dataset>
56+
57+
<dcat:service>
58+
<dcat:DataService rdf:about="http://dcat-ap-es.ejemplo.org/dataservice/dataservice-ejemplo-1">
59+
<dct:title xml:lang="es">Data Service de ejemplo</dct:title>
60+
<dct:title xml:lang="en">Example Data Service</dct:title>
61+
<dct:description xml:lang="es">Descripción de un dataset de ejemplo.</dct:description>
62+
<dct:description xml:lang="en">Example Data Service description.</dct:description>
63+
<dcat:endpointURL rdf:resource="http://api.dcat-ap-es.ejemplo.org/dataservice/dataservice-ejemplo-1"/>
64+
</dcat:DataService>
65+
</dcat:service>
66+
67+
</dcat:Catalog>
68+
69+
</rdf:RDF>

docker-compose.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
services:
2+
rdf-endpoint:
3+
build:
4+
context: .
5+
dockerfile: docker/Dockerfile
6+
env_file:
7+
- .env
8+
logging:
9+
driver: "json-file"
10+
options:
11+
max-size: "10m"
12+
max-file: "5"
13+
ports:
14+
- "${SV_PORT_HOST}:5000"
15+
volumes:
16+
- ./data/${CATALOG_FILE}:/app/catalog.rdf
17+
healthcheck:
18+
test: ["CMD", "curl", "-f", "http://localhost:5000/catalog.rdf"]
19+
interval: 30s
20+
timeout: 10s
21+
retries: 3

docker/Dockerfile

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
FROM python:3.11-slim
2+
LABEL maintainer="mjanez"
3+
LABEL version="1.0"
4+
LABEL description="Easy RDF Endpoint"
5+
6+
ENV CATALOG_FILE=catalog.rdf
7+
ENV WORKERS=2
8+
ENV ENABLE_CORS=false
9+
ENV ALLOWED_ORIGINS="*"
10+
11+
WORKDIR /app
12+
13+
# Create non-root user
14+
RUN groupadd -r appuser && useradd -r -g appuser appuser
15+
16+
COPY ./src /app/
17+
RUN pip install --no-cache-dir -r requirements.txt
18+
COPY ./docker/docker-entrypoint.d/start_server.sh /app/start_server.sh
19+
COPY ./docker/setup/index.html /app/static/index.html
20+
COPY ./docker/setup/robots.txt /app/static/robots.txt
21+
RUN chmod +x /app/start_server.sh
22+
23+
RUN pip install --no-cache-dir --upgrade pip && \
24+
mkdir -p /app/static && \
25+
chown -R appuser:appuser /app
26+
27+
# Change to non-root user
28+
USER appuser
29+
30+
# Copy non-root files
31+
COPY ./data/${CATALOG_FILE} /app/static/catalog.rdf
32+
33+
EXPOSE 5000
34+
35+
CMD ["/app/start_server.sh"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/bash
2+
exec gunicorn --bind 0.0.0.0:5000 --workers $SV_WORKERS app:app

0 commit comments

Comments
 (0)