Skip to content

Commit 26fb21a

Browse files
JenSeRealJens Plüddemann
and
Jens Plüddemann
authored
Adds grafana organization support (#11)
* adds grafana organization support * update dependencies * adds init.py to install script properly * update dockerfile * add github action to build and push docker image * use the same path in Dockerfile as in makefile * initial commit * changed repository to novatec repo --------- Co-authored-by: Jens Plüddemann <[email protected]>
1 parent b9fe531 commit 26fb21a

11 files changed

+212
-21
lines changed

.dockerignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
*.yml
2-
*.csv
31
script/__pycache__**
2+
**/*.md
3+
.gitignore
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
name: Create and publish a Docker image
2+
3+
on:
4+
push:
5+
branches: ["**"] # run for any commit or tag push on any branch
6+
tags-ignore: ["v**"] # reserve v* tags for releases
7+
8+
env:
9+
REGISTRY_IMAGE: ghcr.io/novatecconsulting/grafana-ldap-sync-script
10+
11+
jobs:
12+
build:
13+
name: Build and publish docker image for grafana-ldap-sync-script on ${{ matrix.platform }}
14+
runs-on: ${{ matrix.os }}
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
platform:
19+
- linux/amd64
20+
- linux/arm64
21+
include:
22+
- platform: linux/arm64
23+
os: [ubuntu-latest]
24+
- platform: linux/amd64
25+
os: [ubuntu-latest]
26+
permissions:
27+
contents: read
28+
packages: write
29+
30+
steps:
31+
- name: Checkout repository
32+
uses: actions/checkout@v4
33+
34+
- name: Prepare for grafana-ldap-sync-script on ${{ matrix.platform }}
35+
run: |
36+
platform=${{ matrix.platform }}
37+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
38+
39+
- name: Extract metadata (tags, labels) for Docker from grafana-ldap-sync-script
40+
id: meta
41+
uses: docker/metadata-action@v5
42+
with:
43+
images: ${{ env.REGISTRY_IMAGE }}
44+
tags: |
45+
type=ref,event=branch
46+
type=ref,event=tag
47+
type=ref,event=pr
48+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
49+
50+
- name: Set up QEMU
51+
uses: docker/setup-qemu-action@v3
52+
53+
- name: Set up Docker Buildx
54+
id: buildx
55+
uses: docker/setup-buildx-action@v3
56+
57+
- name: Login to GitHub Container Registry
58+
uses: docker/login-action@v3
59+
with:
60+
registry: ghcr.io
61+
username: ${{ github.actor }}
62+
password: ${{ secrets.GITHUB_TOKEN }}
63+
64+
- name: Build and push by digest for grafana-ldap-sync-script ${{ matrix.platform }}
65+
id: build
66+
uses: docker/build-push-action@v6
67+
with:
68+
context: .
69+
platforms: ${{ matrix.platform }}
70+
labels: ${{ steps.meta.outputs.labels }}
71+
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
72+
cache-from: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
73+
cache-to: type=gha,scope=build-${{ env.PLATFORM_PAIR }}
74+
75+
- name: Export digest for grafana-ldap-sync-script on ${{ matrix.platform }}
76+
run: |
77+
mkdir -p /tmp/digests
78+
digest="${{ steps.build.outputs.digest }}"
79+
touch "/tmp/digests/${digest#sha256:}"
80+
81+
- name: Upload digest
82+
uses: actions/upload-artifact@v4
83+
with:
84+
name: digests-grafana-ldap-sync-script-${{ env.PLATFORM_PAIR }}
85+
path: /tmp/digests/*
86+
if-no-files-found: error
87+
retention-days: 1
88+
89+
merge:
90+
runs-on: ubuntu-latest
91+
needs:
92+
- build
93+
strategy:
94+
fail-fast: false
95+
steps:
96+
- name: Download digests
97+
uses: actions/download-artifact@v4
98+
with:
99+
path: /tmp/digests
100+
pattern: digests-grafana-ldap-sync-script*
101+
merge-multiple: true
102+
103+
- name: Set up Docker Buildx
104+
uses: docker/setup-buildx-action@v3
105+
106+
- name: Docker meta
107+
id: meta
108+
uses: docker/metadata-action@v5
109+
with:
110+
images: ${{ env.REGISTRY_IMAGE }}
111+
tags: |
112+
type=ref,event=branch
113+
type=ref,event=tag
114+
type=ref,event=pr
115+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
116+
117+
- name: Login to GitHub Container Registry
118+
uses: docker/login-action@v3
119+
with:
120+
registry: ghcr.io
121+
username: ${{ github.actor }}
122+
password: ${{ secrets.GITHUB_TOKEN }}
123+
124+
- name: Create manifest list and push
125+
working-directory: /tmp/digests
126+
run: |
127+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
128+
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
129+
130+
- name: Inspect image
131+
run: |
132+
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}

Dockerfile

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
1-
FROM python:3.9-slim
1+
FROM python:3.13-slim AS installer
2+
RUN apt-get update
3+
RUN apt-get install -y --no-install-recommends build-essential gcc
24

3-
COPY requirements.txt /requirements.txt
5+
RUN python -m venv /opt/venv
6+
# Make sure we use the virtualenv:
7+
ENV PATH="/opt/venv/bin:$PATH"
48

5-
RUN pip install -r /requirements.txt && mkdir /app
9+
WORKDIR /app
10+
11+
COPY requirements.txt .
12+
RUN pip install -r requirements.txt
13+
14+
COPY script ./script
15+
COPY setup.py .
16+
RUN pip install .
17+
18+
FROM python:3.13-slim AS runtime
19+
COPY --from=installer /opt/venv /opt/venv
20+
21+
WORKDIR /data
22+
COPY config.yml .
23+
COPY example.csv /data/bind.csv
624

725
WORKDIR /app
26+
# Make sure we use the virtualenv:
27+
ENV PATH="/opt/venv/bin:$PATH"
28+
29+
COPY run.py .
830

9-
COPY LICENSE run.py /app/
10-
COPY script /app/script
31+
VOLUME [ "/data" ]
1132

12-
ENTRYPOINT [ "python3", "./run.py" ]
33+
CMD [ "python", "run.py", "--config=/data/config.yml", "--bind=/data/bind.csv" ]

config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
config:
22
grafana:
3-
43
# URL of the target grafana-server.
54
url: localhost:3000
65
# Protocol to use when connecting to grafana (http, https)
@@ -9,6 +8,8 @@ config:
98
user: admin
109
# Password of account with admin rights on target grafana-server.
1110
password: admin
11+
# Grafana Organization ID that should be used to insert the teams.
12+
# org_id: 1
1213

1314
ldap:
1415
# Set to True if NTLM should be used for LDAP.

docker-compose.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
services:
2+
grafana:
3+
image: grafana/grafana:11.2.2
4+
restart: unless-stopped
5+
volumes:
6+
- grafana-storage:/var/lib/grafana
7+
healthcheck:
8+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
9+
interval: 1m
10+
timeout: 1s
11+
retries: 3
12+
ports:
13+
- 3000:3000
14+
attach: false
15+
16+
grafana-ldap-sync-script:
17+
build:
18+
dockerfile: Dockerfile
19+
volumes:
20+
- ./example.csv:/data/bind.csv:ro
21+
# you need to change grafana.config.url to 'grafana:3000' from 'localhost:3000' for this to work!
22+
- ./config.yml:/data/config.yml:ro
23+
depends_on:
24+
grafana:
25+
condition: service_healthy
26+
27+
28+
volumes:
29+
grafana-storage:

requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
requests~=2.24.0
2-
grafana_client~=2.0.2
3-
ldap3~=2.6
4-
PyYAML~=5.3.1
5-
pyasn1>=0.4.6
1+
requests~=2.32.3
2+
grafana_client~=4.2.0
3+
ldap3~=2.9.1
4+
PyYAML~=6.0.2
5+
pyasn1>=0.4.6

run.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from script.core import startUserSync
21
import argparse
32
import logging
43

4+
from script.core import startUserSync
5+
6+
57
class DispatchingFormatter:
68
def __init__(self, formatters, default_formatter):
79
self._formatters = formatters
@@ -23,7 +25,7 @@ def setup_logger():
2325
else:
2426
log_format_mut = log_format
2527

26-
28+
2729
logger = logging.getLogger()
2830
while logger.handlers:
2931
logger.handlers.pop()

script/__init__.py

Whitespace-only changes.

script/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
23
import yaml
34

45
logging.basicConfig(level=logging.INFO)
@@ -15,6 +16,7 @@ def __init__(self, config_path):
1516
GRAFANA_AUTH = ""
1617
GRAFANA_URL = ""
1718
GRAFANA_PROTOCOL = "http"
19+
GRAFANA_ORG_ID = None
1820

1921
LDAP_SERVER_URL = ""
2022
LDAP_PORT = ""
@@ -49,6 +51,7 @@ def load_config(self, config_path):
4951
self.GRAFANA_URL = config["grafana"]["url"]
5052
if config["grafana"]["protocol"]:
5153
self.GRAFANA_PROTOCOL = config["grafana"]["protocol"]
54+
self.GRAFANA_ORG_ID = config["grafana"]["org_id"] if "org_id" in config["grafana"] else None
5255

5356
self.LDAP_SERVER_URL = config["ldap"]["url"]
5457
self.LDAP_PORT = config["ldap"]["port"]

script/grafana.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from grafana_client.client import GrafanaClientError, GrafanaBadInputError
1+
import logging
2+
23
from grafana_client import GrafanaApi
4+
from grafana_client.client import GrafanaBadInputError, GrafanaClientError
5+
36
from .config import *
47
from .helpers import *
5-
import logging
68

79
grafana_api = ""
810
configuration = ""
@@ -16,7 +18,8 @@ def setup_grafana(config_dict):
1618
grafana_api = GrafanaApi(
1719
auth=configuration.GRAFANA_AUTH,
1820
host=configuration.GRAFANA_URL,
19-
protocol=configuration.GRAFANA_PROTOCOL
21+
protocol=configuration.GRAFANA_PROTOCOL,
22+
organization_id=configuration.GRAFANA_ORG_ID
2023
)
2124

2225

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from setuptools import setup, find_packages
1+
from setuptools import find_packages, setup
22

33
setup(
4-
name='grafana-ldap-sync-script',
4+
name='script',
55
version='1.1.0',
66
description='Script for syncing LDAP Users & Groups with Grafana Users & Teams',
77
packages=find_packages(exclude=('tests', 'docs')),

0 commit comments

Comments
 (0)