From 23be838ea6e6801409774e3885ded88de9c2d2da Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Tue, 5 Jan 2021 16:58:51 -0600 Subject: [PATCH 01/14] set ldap to use recursive search. Set flags for log level. --- Makefile | 5 +- config.yml | 2 + run.py | 8 +-- script/config.py | 45 +++++++++-------- script/ldap.py | 124 +++++++++++++++++++++++++++++++++++------------ 5 files changed, 129 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index 6643802..d88993b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ DOCKER_REPO ?= DOCKERHUB_USER/grafana-ldap-sync-script DOCKER_TAG ?= v1.0 +CONFIG_DIR ?= ${PWD} init: pip install -r requirements.txt @@ -28,7 +29,7 @@ docker-push: docker-build docker push ${DOCKER_REPO}:${DOCKER_TAG} docker-run: docker-build - docker run --mount 'type=bind,source=${PWD},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} --config /data/config.yml --bind /data/example.csv + docker run --mount 'type=bind,source=${CONFIG_DIR},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} --config /data/config.yml --bind /data/example.csv --log-level=debug docker-explore: docker-build - docker run -it --entrypoint /bin/bash --mount 'type=bind,source=${PWD},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} -o vi + docker run -it --entrypoint /bin/bash --mount 'type=bind,source=${CONFIG_DIR},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} -o vi diff --git a/config.yml b/config.yml index 0e1ef0d..6f95185 100644 --- a/config.yml +++ b/config.yml @@ -23,6 +23,8 @@ config: groupSearchBase: dc=example,dc=com # Filter that should be used for the group search. groupSearchFilter: + # Search recursively through groups + searchRecusrively: False # Search-Base for user objects on the LDAP-Server. userSearchBase: dc=example,dc=com # Filter that should be used for user searches. diff --git a/run.py b/run.py index 2ed71ec..ac4be38 100644 --- a/run.py +++ b/run.py @@ -11,7 +11,7 @@ def format(self, record): formatter = self._formatters.get(record.name, self._default_formatter) return formatter.format(record) -def setup_logger(): +def setup_logger(log_level = "INFO"): """ Setting up the used logger. The 'mutate' logger will print whether dry-run is used and changes are being applied. """ @@ -35,13 +35,15 @@ def setup_logger(): ) handler = logging.StreamHandler() handler.setFormatter(formatter) - logger.setLevel(logging.DEBUG) + + logger.setLevel(logging.getLevelName(log_level)) logger.addHandler(handler) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Process config path') parser.add_argument('--config', dest='config_path', help='Path to the config_mock.yml file', required=True) parser.add_argument('--bind', dest='bind', help='Path to the binding file', required=True) + parser.add_argument('--log-level', dest='log_level', help='Log level to use: DEBUG, INFO, WARN, ERROR', required=False, default="INFO") parser.add_argument('--dry-run', dest='dry_run', default=False, help='If this flag is set, the script does not ' 'apply changes to grafana. The would-be ' 'changes are printed to the console.', @@ -49,7 +51,7 @@ def setup_logger(): args = parser.parse_args() # setup the logger - setup_logger() + setup_logger(log_level=args.log_level.upper()) # starts the sync process startUserSync(args.config_path, args.bind, args.dry_run) diff --git a/script/config.py b/script/config.py index 1f54b75..414ae4d 100644 --- a/script/config.py +++ b/script/config.py @@ -23,6 +23,7 @@ def __init__(self, config_path): LDAP_GROUP_SEARCH_BASE = "" LDAP_GROUP_DESCRIPTOR = "" LDAP_GROUP_SEARCH_FILTER = "" + LDAP_SEARCH_RECURSIVELY = False LDAP_MEMBER_ATTRIBUTE = "" LDAP_IS_NTLM = False LDAP_USE_SSL = False @@ -31,6 +32,7 @@ def __init__(self, config_path): LDAP_USER_MAIL_ATTRIBUTE = "" LDAP_USER_SEARCH_BASE = "" LDAP_USER_SEARCH_FILTER = "" + DRY_RUN = False def load_config(self, config_path): @@ -42,26 +44,29 @@ def load_config(self, config_path): except FileNotFoundError as e: logger.error("Config-file %s does not exist!", config_path) raise e + + grafana_config = config.get("grafana", {}) self.GRAFANA_AUTH = ( - config["grafana"]["user"], - config["grafana"]["password"] + grafana_config.get("user", ""), + grafana_config.get("password", "") ) - self.GRAFANA_URL = config["grafana"]["url"] - if config["grafana"]["protocol"]: - self.GRAFANA_PROTOCOL = config["grafana"]["protocol"] + self.GRAFANA_URL = grafana_config.get("url", self.GRAFANA_URL) + self.GRAFANA_PROTOCOL = grafana_config.get("protocol", self.GRAFANA_PROTOCOL) - self.LDAP_SERVER_URL = config["ldap"]["url"] - self.LDAP_PORT = config["ldap"]["port"] - self.LDAP_USER = config["ldap"]["login"] - self.LDAP_PASSWORD = config["ldap"]["password"] - self.LDAP_GROUP_SEARCH_BASE = config["ldap"]["groupSearchBase"] - self.LDAP_GROUP_SEARCH_FILTER = config["ldap"]["groupSearchFilter"] - self.LDAP_MEMBER_ATTRIBUTE = config["ldap"]["memberAttributeName"] - self.LDAP_USER_LOGIN_ATTRIBUTE = config["ldap"]["userLoginAttribute"] - self.LDAP_IS_NTLM = config["ldap"]["useNTLM"] - self.LDAP_USE_SSL = config["ldap"]["useSSL"] - self.LDAP_USER_LOGIN_ATTRIBUTE = config["ldap"]["userLoginAttribute"] - self.LDAP_USER_NAME_ATTRIBUTE = config["ldap"]["userNameAttribute"] - self.LDAP_USER_MAIL_ATTRIBUTE = config["ldap"]["userMailAttribute"] - self.LDAP_USER_SEARCH_BASE = config["ldap"]["userSearchBase"] - self.LDAP_USER_SEARCH_FILTER = config["ldap"]["userSearchFilter"] + ldap_config = config.get("ldap", {}) + self.LDAP_SERVER_URL = ldap_config.get("url", self.LDAP_SERVER_URL) + self.LDAP_PORT = ldap_config.get("port", self.LDAP_PORT) + self.LDAP_USER = ldap_config.get("login", self.LDAP_USER) + self.LDAP_PASSWORD = ldap_config.get("password", self.LDAP_PASSWORD) + self.LDAP_GROUP_SEARCH_BASE = ldap_config.get("groupSearchBase", self.LDAP_GROUP_SEARCH_BASE) + self.LDAP_GROUP_SEARCH_FILTER = ldap_config.get("groupSearchFilter", self.LDAP_GROUP_SEARCH_FILTER) + self.LDAP_MEMBER_ATTRIBUTE = ldap_config.get("memberAttributeName", self.LDAP_MEMBER_ATTRIBUTE) + self.LDAP_USER_LOGIN_ATTRIBUTE = ldap_config.get("userLoginAttribute", self.LDAP_USER_LOGIN_ATTRIBUTE) + self.LDAP_IS_NTLM = ldap_config.get("useNTLM", self.LDAP_IS_NTLM) + self.LDAP_USE_SSL = ldap_config.get("useSSL", self.LDAP_USE_SSL) + self.LDAP_USER_LOGIN_ATTRIBUTE = ldap_config.get("userLoginAttribute", self.LDAP_USER_LOGIN_ATTRIBUTE) + self.LDAP_USER_NAME_ATTRIBUTE = ldap_config.get("userNameAttribute", self.LDAP_USER_NAME_ATTRIBUTE) + self.LDAP_USER_MAIL_ATTRIBUTE = ldap_config.get("userMailAttribute", self.LDAP_USER_MAIL_ATTRIBUTE) + self.LDAP_USER_SEARCH_BASE = ldap_config.get("userSearchBase", self.LDAP_USER_SEARCH_BASE) + self.LDAP_USER_SEARCH_FILTER = ldap_config.get("userSearchFilter", self.LDAP_USER_SEARCH_FILTER) + self.LDAP_SEARCH_RECURSIVELY = ldap_config.get("searchRecursively", self.LDAP_SEARCH_RECURSIVELY) diff --git a/script/ldap.py b/script/ldap.py index 3304cad..f95f34a 100644 --- a/script/ldap.py +++ b/script/ldap.py @@ -7,7 +7,8 @@ logger = logging.getLogger() configuration = "" -user_cache = {} +group_cache = {} +member_cache = {} connection = "" @@ -53,47 +54,50 @@ def fetch_users_of_group(group_name): """ logger.info("Fetching users of ldap group %s " % group_name) result = [] - connection.bind() + i_bound_connection = False + if not connection.bound: + i_bound_connection = True + connection.bind() + if configuration.LDAP_GROUP_SEARCH_FILTER: group_query_filter = "(&(cn=" + group_name + ")" + configuration.LDAP_GROUP_SEARCH_FILTER + ")" else: group_query_filter = "(cn=" + group_name + ")" + groups = connection.extend.standard.paged_search(search_base=configuration.LDAP_GROUP_SEARCH_BASE, search_filter=group_query_filter, search_scope=SUBTREE, attributes=[configuration.LDAP_MEMBER_ATTRIBUTE], paged_size=5, generator=True) + for group in groups: - for user in group["attributes"][configuration.LDAP_MEMBER_ATTRIBUTE]: + for member_dn in group["attributes"][configuration.LDAP_MEMBER_ATTRIBUTE]: if configuration.LDAP_USER_SEARCH_FILTER: user_query_filter = configuration.LDAP_USER_SEARCH_FILTER else: user_query_filter = "(objectClass=*)" - logger.info("Fetching user %s of ldap group %s " % (user, group_name)) - user_data = connection.extend.standard.paged_search(search_base=user, - search_scope=SUBTREE, - search_filter=user_query_filter, - attributes=[configuration.LDAP_USER_LOGIN_ATTRIBUTE, - configuration.LDAP_USER_NAME_ATTRIBUTE, - configuration.LDAP_USER_MAIL_ATTRIBUTE], - paged_size=5) - for user_set in user_data: - data_set = user_set["attributes"] - login = data_set[configuration.LDAP_USER_LOGIN_ATTRIBUTE] - name = data_set[configuration.LDAP_USER_NAME_ATTRIBUTE] - mail = data_set[configuration.LDAP_USER_MAIL_ATTRIBUTE] - if not login or not name: - continue - if not mail: - mail = login - result.append({ - "login": login, - "name": name, - "email": mail - }) - connection.unbind() - return result + + user = get_member(member_dn) + + if not user: + continue + + # Handle nested groups + if "group" in user["objectClass"]: + if configuration.LDAP_SEARCH_RECURSIVELY: + result = result + get_users_of_group(user["login"]) + continue + + result.append(user) + + if i_bound_connection: + connection.unbind() + + # Filter out any duplicates from the result array + # Also we remove the object class key + distinct_results = list({u["login"]: {i:u[i] for i in u if i != "objectClass"} for u in result}.values()) + return distinct_results def get_users_of_group(group): @@ -105,6 +109,66 @@ def get_users_of_group(group): :return: A list containing dictionaries. Each dictionary consists of a login attribute and the respective user login. """ - if group not in user_cache: - user_cache[group] = fetch_users_of_group(group) - return user_cache[group] + if group not in group_cache: + group_cache[group] = fetch_users_of_group(group) + return group_cache[group] + +def get_member(member_dn): + """ + Gets a member and uses a member_cache to speed things up. These members are the members fetched for groups + :param member_dn: The DN of the member to fetch + :return: A single member result + login. + """ + if member_dn not in member_cache: + member_cache[member_dn] = fetch_member(member_dn) + return member_cache[member_dn] + +def fetch_member(member_dn): + """ + Fetches an individual member from ldap + :param member_dn: The DN of the member + :return: A single member result + """ + logger.info("Fetching member %s " % member_dn) + + i_bound_connection = False + if not connection.bound: + i_bound_connection = True + connection.bind() + + if configuration.LDAP_USER_SEARCH_FILTER: + member_query_filter = configuration.LDAP_USER_SEARCH_FILTER + else: + member_query_filter = "(objectClass=*)" + + member_data = connection.extend.standard.paged_search(search_base=member_dn, + search_scope=SUBTREE, + search_filter=member_query_filter, + attributes=[configuration.LDAP_USER_LOGIN_ATTRIBUTE, + configuration.LDAP_USER_NAME_ATTRIBUTE, + configuration.LDAP_USER_MAIL_ATTRIBUTE, + "objectClass"], + paged_size=5) + for member_set in member_data: + data_set = member_set["attributes"] + login = data_set[configuration.LDAP_USER_LOGIN_ATTRIBUTE] + name = data_set[configuration.LDAP_USER_NAME_ATTRIBUTE] + mail = data_set[configuration.LDAP_USER_MAIL_ATTRIBUTE] + objectClass = data_set["objectClass"] + + if not login or not name: + return None + + if not mail: + mail = login + + if i_bound_connection: + connection.unbind() + + return { + "login": login, + "name": name, + "email": mail, + "objectClass": objectClass + } From 12855299bd9b112da46fee62ee6cd7759b09a1fc Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 13:31:05 -0600 Subject: [PATCH 02/14] Added helm chart --- deploy/helm/grafana-ldap-sync/.helmignore | 23 +++ deploy/helm/grafana-ldap-sync/Chart.yaml | 23 +++ deploy/helm/grafana-ldap-sync/Makefile | 2 + .../grafana-ldap-sync/templates/NOTES.txt | 1 + .../grafana-ldap-sync/templates/_helpers.tpl | 62 ++++++ .../grafana-ldap-sync/templates/config.yaml | 74 +++++++ .../grafana-ldap-sync/templates/cronjob.yaml | 97 +++++++++ .../templates/serviceaccount.yaml | 12 ++ .../grafana-ldap-sync/values.example.yaml | 48 +++++ deploy/helm/grafana-ldap-sync/values.yaml | 184 ++++++++++++++++++ 10 files changed, 526 insertions(+) create mode 100644 deploy/helm/grafana-ldap-sync/.helmignore create mode 100644 deploy/helm/grafana-ldap-sync/Chart.yaml create mode 100644 deploy/helm/grafana-ldap-sync/Makefile create mode 100644 deploy/helm/grafana-ldap-sync/templates/NOTES.txt create mode 100644 deploy/helm/grafana-ldap-sync/templates/_helpers.tpl create mode 100644 deploy/helm/grafana-ldap-sync/templates/config.yaml create mode 100644 deploy/helm/grafana-ldap-sync/templates/cronjob.yaml create mode 100644 deploy/helm/grafana-ldap-sync/templates/serviceaccount.yaml create mode 100644 deploy/helm/grafana-ldap-sync/values.example.yaml create mode 100644 deploy/helm/grafana-ldap-sync/values.yaml diff --git a/deploy/helm/grafana-ldap-sync/.helmignore b/deploy/helm/grafana-ldap-sync/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml new file mode 100644 index 0000000..48af5d6 --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -0,0 +1,23 @@ +apiVersion: v2 +name: grafana-ldap-sync +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +appVersion: 1.16.0 diff --git a/deploy/helm/grafana-ldap-sync/Makefile b/deploy/helm/grafana-ldap-sync/Makefile new file mode 100644 index 0000000..b07a993 --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/Makefile @@ -0,0 +1,2 @@ +template: + helm3 template . --values=./values.example.yaml --debug diff --git a/deploy/helm/grafana-ldap-sync/templates/NOTES.txt b/deploy/helm/grafana-ldap-sync/templates/NOTES.txt new file mode 100644 index 0000000..0510505 --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/templates/NOTES.txt @@ -0,0 +1 @@ +This helm chart deploys a cronjob which runs on, by default, a 30 minute schedule. diff --git a/deploy/helm/grafana-ldap-sync/templates/_helpers.tpl b/deploy/helm/grafana-ldap-sync/templates/_helpers.tpl new file mode 100644 index 0000000..50144ee --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "grafana-ldap-sync.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "grafana-ldap-sync.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "grafana-ldap-sync.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "grafana-ldap-sync.labels" -}} +helm.sh/chart: {{ include "grafana-ldap-sync.chart" . }} +{{ include "grafana-ldap-sync.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "grafana-ldap-sync.selectorLabels" -}} +app.kubernetes.io/name: {{ include "grafana-ldap-sync.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "grafana-ldap-sync.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "grafana-ldap-sync.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/deploy/helm/grafana-ldap-sync/templates/config.yaml b/deploy/helm/grafana-ldap-sync/templates/config.yaml new file mode 100644 index 0000000..c78e722 --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/templates/config.yaml @@ -0,0 +1,74 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "grafana-ldap-sync.fullname" . }}-config + labels: + {{- include "grafana-ldap-sync.labels" . | nindent 4 }} +stringData: + config.yml: | + config: + {{- .Values.config | toYaml | nindent 6 }} + {{- if .Values.csvContent }} + permissions.csv: {{ .Values.csvContent | quote }} + {{- else }} + {{- $comment := "In this case we need to generate the CSV usign some crazy logic" }} + permissions.csv: | + {{- $perms := list ( list "ZBV/LDAP-Gruppe" "Grafana-Team-Name" "Grafana-Team-ID" "Grafana-Folder-Name" "Grafana-Folder-UUID" "Grafana-Folder-Permissions" ) }} + {{- $teams := .Values.teams }} + {{- $folders := .Values.folders }} + {{- $addTeamsToAll := .Values.addTeamsToAll }} + {{- range $teamName, $groups := $teams }} + {{- $comment := "This is how we default the group list to the team name" }} + {{- if not $groups }} + {{- $groups = list $teamName }} + {{- end }} + + {{- $comment := "Ensure that every permission level exists for every folder" }} + {{- range $permLevel, $teamsToAdd := $addTeamsToAll }} + {{- range $folderName, $permission := $folders }} + {{- if not ( get $permission $permLevel ) }} + {{- $permission = set $permission $permLevel ( list ) }} + {{- $folders = set $folders $folderName $permission }} + {{- end }} + {{- end }} + {{- end }} + + {{- range $folderName, $permission := $folders }} + {{- $comment := "This is how we default the permission" }} + {{- if not $permission }} + {{- $permission = dict "admin" ( list $folderName ) }} + {{- end }} + + {{- range $permLevel, $permTeams := $permission }} + {{- $comment := "If the user decides to use Admin we must ensure it is lowercase" }} + {{- $permLevel = lower $permLevel }} + + {{- $comment := "This is how we use the addTeamsToAll so that a team can be added to all folders" }} + + {{- $teamsToAdd := ( get $addTeamsToAll $permLevel ) }} + {{- if $teamsToAdd }} + {{- $permTeams = concat $permTeams $teamsToAdd }} + {{- end }} + + {{- range $t := $permTeams }} + {{- if eq ( toString $t ) $teamName }} + {{- range $group := $groups }} + {{- $ldapGroup := $group }} + {{- $grafanaTeamName := $teamName }} + {{- $grafanaTeamID := 1 }} + {{- $grafanaFolderName := $folderName }} + {{- $grafanaFolderUUID := $folderName | lower | replace " " "-" }} + {{- $grafanaFolderPermissions := title $permLevel }} + + {{- $perms = append $perms ( list $ldapGroup $grafanaTeamName $grafanaTeamID $grafanaFolderName $grafanaFolderUUID $grafanaFolderPermissions ) }} + {{- end }} {{- $comment := "End range $groups" }} + {{- end }} {{- $comment := "end if eq $teamname" }} + {{- end }} {{- $comment := "end range $teams" }} + {{- end }} {{- $comment := "end range $permission" }} + {{- end }} {{- $comment := "end range $folders" }} + {{- end }} {{- $comment := "end range $teams" }} + {{- range $perm := $perms }} + {{- $perm | join "," | nindent 4 }} + {{- end }} + {{- end }} {{- $comment := "End if .csvContent" }} + diff --git a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml new file mode 100644 index 0000000..02b5576 --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml @@ -0,0 +1,97 @@ +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: {{ include "grafana-ldap-sync.fullname" . }} + labels: + {{- include "grafana-ldap-sync.labels" . | nindent 4 }} +spec: + startingDeadlineSeconds: {{ .Values.cronJob.startingDeadlineSeconds }} + schedule: {{ .Values.cronJob.schedule | quote }} + successfulJobsHistoryLimit: {{ .Values.cronJob.successfulJobsHistoryLimit }} + suspend: {{ .Values.cronJob.suspend }} + concurrencyPolicy: {{ .Values.cronJob.concurrencyPolicy }} + failedJobsHistoryLimit: {{ .Values.cronJob.failedJobsHistoryLimit }} + jobTemplate: + metadata: + labels: + {{- include "grafana-ldap-sync.labels" . | nindent 8 }} + spec: + activeDeadlineSeconds: 120 + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 12 }} + {{- end }} + labels: + {{- include "grafana-ldap-sync.selectorLabels" . | nindent 12 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 12 }} + {{- end }} + serviceAccountName: {{ include "grafana-ldap-sync.serviceAccountName" . }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.extraInitContainers }} + {{- toYaml . | nindent 10 }} + {{- end }} + containers: + {{- with .Values.extraContainers }} + {{- toYaml . | nindent 12 }} + {{- end }} + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 16 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --config-file=/config/config.yaml + - --bind=/config/permissions.csv + - --log-level={{ .Values.logLevel }} + {{- if .Values.dryRun }} + - --dry-run + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 16 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /config + readOnly: true + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 14 }} + {{- end }} + {{- with .Values.extraContainerConfig }} + {{- toYaml . | nindent 14 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: config + secret: + secretName: {{ include "grafana-ldap-sync.fullname" . }}-config + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 12 }} + {{- end }} +--- +{{- range $man := .Values.extraManifests }} +{{- toYaml $man }} +--- +{{- end }} + diff --git a/deploy/helm/grafana-ldap-sync/templates/serviceaccount.yaml b/deploy/helm/grafana-ldap-sync/templates/serviceaccount.yaml new file mode 100644 index 0000000..325dcef --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "grafana-ldap-sync.serviceAccountName" . }} + labels: + {{- include "grafana-ldap-sync.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/grafana-ldap-sync/values.example.yaml b/deploy/helm/grafana-ldap-sync/values.example.yaml new file mode 100644 index 0000000..de890af --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/values.example.yaml @@ -0,0 +1,48 @@ +# Map of orgs -> teams -> ldap groups +# If the array of groups is empty, uses the team name as the group name +# $GRAFANA_TEAM_NAME: +# - $FIRST_LDAP_GROUP +# - $SECOND_LDAP_GROUP +# +# Like the CSV example above: (YAML allows spaces) +teams: + smart people: + - chemists + - mathematicians + - scientists + mathematicians: [] # Same as: mathematicians: [ "mathematicians" ] + admin team: + - devops + +# Maps folders -> permissions -> teams +# Permission should be one of: admin, editor, viewer +# $GRAFANA_FOLDER_NAME: +# $PERMISSION: +# - $TEAM_NAME_ONE +# - $TEAM_NAME_TWO +# +# If the permission's list is empty, then it defuaults to: +# $GRAFANA_FOLDER:ANME: +# admin: +# - $GRAFANA_FOLDER_NAME +# +folders: + all: + admin: + - mathematicians + view: + - smart people + mathematicians: {} # Same as: mathematicians: { "admin": [ "mathematicians" ] } + +# If you want to add a user team to have a specific permission to all folders then you can add them to this list +# The teams in each array will be appended to the list of teams above (in `folders`) +# +# addTeamsToAll: +# $PERMISSION: +# - $TEAM_NAME_ONE +# - $TEAM_NAME_TWO +addTeamsToAll: + admin: [] + editor: + - admin team + viewer: [] diff --git a/deploy/helm/grafana-ldap-sync/values.yaml b/deploy/helm/grafana-ldap-sync/values.yaml new file mode 100644 index 0000000..42cf96a --- /dev/null +++ b/deploy/helm/grafana-ldap-sync/values.yaml @@ -0,0 +1,184 @@ +# Default values for grafana-ldap-sync. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: DOCKERHUB_USER/grafana-ldap-sync-script + pullPolicy: Always + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# If you want to customize very finely what is deployed, these can help: +# Add volumes +extraVolumes: [] +# Add volume mounts +extraVolumeMounts: [] +# Add initContainers +extraInitContainers: [] +# Add sidecar containers +extraContainers: [] +# Add additional config options to the main container +extraContainerConfig: {} +# Add additional fully-baked manifests to the chart +extraManifests: [] + +cronJob: + startingDeadlineSeconds: 60 + schedule: "*/30 * * * *" + successfulJobsHistoryLimit: 3 + suspend: false + concurrencyPolicy: Forbid + failedJobsHistoryLimit: 1 + +logLevel: info +dryRun: true + +# Config maps to the config for the grafana ldap sync script. Content will be saved verbatim. +config: + grafana: + # URL of the target grafana-server. + url: localhost:3000 + # Protocol to use when connecting to grafana (http, https) + protocol: http + # User account name with admin rights on target grafana-server. + user: admin + # Password of account with admin rights on target grafana-server. + password: admin + + ldap: + # Set to True if NTLM should be used for LDAP. + useNTLM: True + # If True, SSL will be used for connection to LDAP. + useSSL: False + # URL of the source LDAP-Server. + url: ldap.forumsys.com + # Port of the ldap instance provided in the url-variable. + port: 389 + # Group search-base of the source LDAP-Server. + groupSearchBase: dc=example,dc=com + # Filter that should be used for the group search. + groupSearchFilter: + # Search recursively through groups + searchRecusrively: False + # Search-Base for user objects on the LDAP-Server. + userSearchBase: dc=example,dc=com + # Filter that should be used for user searches. + userSearchFilter: + # Attribute name under which the members of a group object can be found. + memberAttributeName: uniqueMember + # Name of the attribute which should be used as login in grafana. + userLoginAttribute: uid + # Name of the attribute which should be used as Name in grafana. + userNameAttribute: cn + # Name of the attribute which should be used as mail in grafana. + userMailAttribute: mail + # Login for the LDAP-Server. + login: test\cn=read-only-admin,dc=example,dc=com + # Password for the LDAP-Server. + password: password + + +# If you want to set the CSV Content explicitly set the content of csvContent to a string. +# If this is blank, then we'll use the `teams` and `folders` maps +# Of note, the Grafana-Team-ID value is not used so you can ignore that or set it to 1 +# csvContent: | +# ZBV/LDAP-Gruppe,Grafana-Team-Name,Grafana-Team-ID,Grafana-Folder-Name,Grafana-Folder-UUID,Grafana-Folder-Permissions +# mathematicians,mathematicians,1,math,math_folder,Admin +# mathematicians,smart people,1,all,all,View +# scientists,smart people,1,all,all,View +# chemists,smart people,1,all,all,View +csvContent: "" + +# Map of orgs -> teams -> ldap groups +# If the array of groups is empty, uses the team name as the group name +# $GRAFANA_TEAM_NAME: +# - $FIRST_LDAP_GROUP +# - $SECOND_LDAP_GROUP +# +# Like the CSV example above: (YAML allows spaces) +# teams: +# smart people: +# - chemists +# - mathematicians +# - scientists +# mathematicians: [] # Same as: mathematicians: [ "mathematicians" ] +teams: {} + +# Maps folders -> permissions -> teams +# Permission should be one of: admin, editor, viewer +# $GRAFANA_FOLDER_NAME: +# $PERMISSION: +# - $TEAM_NAME_ONE +# - $TEAM_NAME_TWO +# +# If the permission's list is empty, then it defuaults to: +# $GRAFANA_FOLDER:ANME: +# admin: +# - $GRAFANA_FOLDER_NAME +# +# Like the CSV example above: +# all: +# admin: +# - mathematicians +# view: +# - smart people +# mathematicians: {} # Same as: mathematicians: { "admin": [ "mathematicians" ] } +folders: {} + +# If you want to add a user team to have a specific permission to all folders then you can add them to this list +# The teams in each array will be appended to the list of teams above (in `folders`) +# +# addTeamsToAll: +# $PERMISSION: +# - $TEAM_NAME_ONE +# - $TEAM_NAME_TWO +addTeamsToAll: + admin: [] + editor: [] + viewer: [] From ef4919c1140c5d4b981594232d2d3708464f6856 Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 13:54:38 -0600 Subject: [PATCH 03/14] bumped chart version --- deploy/helm/grafana-ldap-sync/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml index 48af5d6..0fd3d33 100644 --- a/deploy/helm/grafana-ldap-sync/Chart.yaml +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -15,9 +15,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 1.0.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. -appVersion: 1.16.0 +appVersion: 1.0.0 From a2953c43833ae16216ee8d6949cb339dceccd1a9 Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 14:21:20 -0600 Subject: [PATCH 04/14] fxied restart policy --- deploy/helm/grafana-ldap-sync/Chart.yaml | 2 +- deploy/helm/grafana-ldap-sync/templates/cronjob.yaml | 1 + deploy/helm/grafana-ldap-sync/values.yaml | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml index 0fd3d33..9aa5d06 100644 --- a/deploy/helm/grafana-ldap-sync/Chart.yaml +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.0 +version: 1.0.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml index 02b5576..28421f8 100644 --- a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml +++ b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml @@ -31,6 +31,7 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} serviceAccountName: {{ include "grafana-ldap-sync.serviceAccountName" . }} + restartPolicy: {{ .Values.restartPolicy }} {{- with .Values.securityContext }} securityContext: {{- toYaml . | nindent 12 }} diff --git a/deploy/helm/grafana-ldap-sync/values.yaml b/deploy/helm/grafana-ldap-sync/values.yaml index 42cf96a..bf6aba6 100644 --- a/deploy/helm/grafana-ldap-sync/values.yaml +++ b/deploy/helm/grafana-ldap-sync/values.yaml @@ -68,6 +68,9 @@ extraContainerConfig: {} # Add additional fully-baked manifests to the chart extraManifests: [] +# Restart Policy can either be Never or OnFailure +restartPolicy: Never + cronJob: startingDeadlineSeconds: 60 schedule: "*/30 * * * *" From 011ae76ff95f80eede1d360a6ec5b0c0fffebeb1 Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 14:29:01 -0600 Subject: [PATCH 05/14] fixed config file line --- deploy/helm/grafana-ldap-sync/Chart.yaml | 2 +- deploy/helm/grafana-ldap-sync/templates/cronjob.yaml | 6 +++++- deploy/helm/grafana-ldap-sync/values.yaml | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml index 9aa5d06..04780f2 100644 --- a/deploy/helm/grafana-ldap-sync/Chart.yaml +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.1 +version: 1.0.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml index 28421f8..48fc2f9 100644 --- a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml +++ b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml @@ -51,12 +51,16 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} args: - - --config-file=/config/config.yaml + - --config=/config/config.yaml - --bind=/config/permissions.csv - --log-level={{ .Values.logLevel }} {{- if .Values.dryRun }} - --dry-run {{- end }} + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 14 }} + {{- end }} + {{- with .Values.resources }} resources: {{- toYaml . | nindent 16 }} diff --git a/deploy/helm/grafana-ldap-sync/values.yaml b/deploy/helm/grafana-ldap-sync/values.yaml index bf6aba6..879f87d 100644 --- a/deploy/helm/grafana-ldap-sync/values.yaml +++ b/deploy/helm/grafana-ldap-sync/values.yaml @@ -67,6 +67,8 @@ extraContainers: [] extraContainerConfig: {} # Add additional fully-baked manifests to the chart extraManifests: [] +# Add more args to the command that's run +extraArgs: [] # Restart Policy can either be Never or OnFailure restartPolicy: Never From c5ce9d733659171761ddbb4a828ae9b6cab1a8ee Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 14:34:16 -0600 Subject: [PATCH 06/14] fixd name of yaml file --- deploy/helm/grafana-ldap-sync/Chart.yaml | 2 +- deploy/helm/grafana-ldap-sync/templates/config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml index 04780f2..3b1f472 100644 --- a/deploy/helm/grafana-ldap-sync/Chart.yaml +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.2 +version: 1.0.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deploy/helm/grafana-ldap-sync/templates/config.yaml b/deploy/helm/grafana-ldap-sync/templates/config.yaml index c78e722..0f79345 100644 --- a/deploy/helm/grafana-ldap-sync/templates/config.yaml +++ b/deploy/helm/grafana-ldap-sync/templates/config.yaml @@ -5,7 +5,7 @@ metadata: labels: {{- include "grafana-ldap-sync.labels" . | nindent 4 }} stringData: - config.yml: | + config.yaml: | config: {{- .Values.config | toYaml | nindent 6 }} {{- if .Values.csvContent }} From 7f5a3317daa7d680a8fd195aaf57cbde2f00856c Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 14:45:35 -0600 Subject: [PATCH 07/14] fixed viewer, editor -> view, edit --- deploy/helm/grafana-ldap-sync/Chart.yaml | 2 +- deploy/helm/grafana-ldap-sync/values.example.yaml | 6 +++--- deploy/helm/grafana-ldap-sync/values.yaml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/deploy/helm/grafana-ldap-sync/Chart.yaml b/deploy/helm/grafana-ldap-sync/Chart.yaml index 3b1f472..b15de94 100644 --- a/deploy/helm/grafana-ldap-sync/Chart.yaml +++ b/deploy/helm/grafana-ldap-sync/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.3 +version: 1.0.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/deploy/helm/grafana-ldap-sync/values.example.yaml b/deploy/helm/grafana-ldap-sync/values.example.yaml index de890af..b3db89c 100644 --- a/deploy/helm/grafana-ldap-sync/values.example.yaml +++ b/deploy/helm/grafana-ldap-sync/values.example.yaml @@ -15,7 +15,7 @@ teams: - devops # Maps folders -> permissions -> teams -# Permission should be one of: admin, editor, viewer +# Permission should be one of: admin, edit, view # $GRAFANA_FOLDER_NAME: # $PERMISSION: # - $TEAM_NAME_ONE @@ -43,6 +43,6 @@ folders: # - $TEAM_NAME_TWO addTeamsToAll: admin: [] - editor: + edit: - admin team - viewer: [] + view: [] diff --git a/deploy/helm/grafana-ldap-sync/values.yaml b/deploy/helm/grafana-ldap-sync/values.yaml index 879f87d..1bb6860 100644 --- a/deploy/helm/grafana-ldap-sync/values.yaml +++ b/deploy/helm/grafana-ldap-sync/values.yaml @@ -156,7 +156,7 @@ csvContent: "" teams: {} # Maps folders -> permissions -> teams -# Permission should be one of: admin, editor, viewer +# Permission should be one of: admin, edit, view # $GRAFANA_FOLDER_NAME: # $PERMISSION: # - $TEAM_NAME_ONE @@ -185,5 +185,5 @@ folders: {} # - $TEAM_NAME_TWO addTeamsToAll: admin: [] - editor: [] - viewer: [] + edit: [] + view: [] From 1de90583ca4ab8cc828332b72f1f95756d099c1f Mon Sep 17 00:00:00 2001 From: Oliver Isaac Date: Wed, 6 Jan 2021 16:31:57 -0600 Subject: [PATCH 08/14] Added exit code logic --- Makefile | 2 +- run.py | 4 +++- script/core.py | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d88993b..2ba9235 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ docker-push: docker-build docker push ${DOCKER_REPO}:${DOCKER_TAG} docker-run: docker-build - docker run --mount 'type=bind,source=${CONFIG_DIR},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} --config /data/config.yml --bind /data/example.csv --log-level=debug + docker run --mount 'type=bind,source=${CONFIG_DIR},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} --config /data/config.yml --bind /data/example.csv --log-level=debug --dry-run docker-explore: docker-build docker run -it --entrypoint /bin/bash --mount 'type=bind,source=${CONFIG_DIR},target=/data' ${DOCKER_REPO}:${DOCKER_TAG} -o vi diff --git a/run.py b/run.py index ac4be38..0091ef0 100644 --- a/run.py +++ b/run.py @@ -1,6 +1,7 @@ from script.core import startUserSync import argparse import logging +import sys class DispatchingFormatter: def __init__(self, formatters, default_formatter): @@ -54,4 +55,5 @@ def setup_logger(log_level = "INFO"): setup_logger(log_level=args.log_level.upper()) # starts the sync process - startUserSync(args.config_path, args.bind, args.dry_run) + exit_code = startUserSync(args.config_path, args.bind, args.dry_run) + sys.exit(exit_code) diff --git a/script/core.py b/script/core.py index 90aa7c2..f4fcb62 100644 --- a/script/core.py +++ b/script/core.py @@ -242,6 +242,7 @@ def startUserSync(config_path, bind, dry_run): folders and users is performed. If a .lock file is present, no action is performed. """ + exit_code=1 global configuration if lock(): try: @@ -270,6 +271,7 @@ def startUserSync(config_path, bind, dry_run): remove_unused_items(mapping["teams"]) logger.info("Task finished successfully!") + exit_code=0 except LDAPSocketOpenError: logger.error("Task aborted, unable to reach LDAP-Server.") except ConnectionError: @@ -279,3 +281,5 @@ def startUserSync(config_path, bind, dry_run): unlock() else: logger.error("Task aborted, process is already active!") + return exit_code + From b69ea47ead0770009624eacf67c1d8594bb2292b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:29:45 +0100 Subject: [PATCH 09/14] add dev environment --- .gitignore | 7 +- flake.lock | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 88 +++++++ 3 files changed, 752 insertions(+), 1 deletion(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 74b4bb7..f030e53 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,9 @@ dmypy.json .lock .idea /shelf/ -/workspace.xml \ No newline at end of file +/workspace.xml + +# Nix +.devenv +.direnv +.envrc \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0dc752a --- /dev/null +++ b/flake.lock @@ -0,0 +1,658 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "git-hooks": [ + "devenv", + "pre-commit-hooks" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726520618, + "narHash": "sha256-jOsaBmJ/EtX5t/vbylCdS7pWYcKGmWOKg4QKUzKr6dA=", + "owner": "cachix", + "repo": "cachix", + "rev": "695525f9086542dfb09fde0871dbf4174abbf634", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "cachix_2": { + "inputs": { + "devenv": "devenv_3", + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "cachix", + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_3", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks_2" + }, + "locked": { + "lastModified": 1730028891, + "narHash": "sha256-SazXjBiyMn7Jt4NWOZ8FWKg+JfoKEWUoKidYF/+zDU4=", + "owner": "cachix", + "repo": "devenv", + "rev": "a337f8862efa9bde348c48a48b5b943f710604dd", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "cachix": "cachix_2", + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix_2", + "nixpkgs": [ + "devenv", + "cachix", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "cachix", + "git-hooks" + ] + }, + "locked": { + "lastModified": 1723156315, + "narHash": "sha256-0JrfahRMJ37Rf1i0iOOn+8Z4CLvbcGNwa2ChOAVrp/8=", + "owner": "cachix", + "repo": "devenv", + "rev": "ff5eb4f2accbcda963af67f1a1159e3f6c7f5f91", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_3": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix_3": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": "nixpkgs-23-11", + "nixpkgs-regression": "nixpkgs-regression_3", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1727438425, + "narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_3": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1729880355, + "narHash": "sha256-RP+OQ6koQQLX5nw0NmcDrzvGL8HDLnyXt/jHhL1jwjM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "18536bf04cd71abd345f9579158841376fdd0c5a", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "nix" + ], + "flake-utils": "flake-utils_2", + "gitignore": [ + "devenv", + "nix" + ], + "nixpkgs": [ + "devenv", + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1726745158, + "narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs_3", + "systems": "systems_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5b3b56f --- /dev/null +++ b/flake.nix @@ -0,0 +1,88 @@ +{ + description = "Basic template for a dev shell"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + systems.url = "github:nix-systems/default"; + devenv.url = "github:cachix/devenv"; + devenv.inputs.nixpkgs.follows = "nixpkgs"; + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; + + outputs = + { + self, + nixpkgs, + devenv, + systems, + ... + }@inputs: + let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in + { + packages = forEachSystem (system: { + devenv-up = self.devShells.${system}.default.config.procfileScript; + }); + devShells = forEachSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + config.allowBroken = true; + }; + setupScript = pkgs.writeShellScriptBin "setup-env" '' + [ ! -f .env ] || export $(grep -v '^#' .env | xargs) + ''; + in + { + default = devenv.lib.mkShell { + inherit inputs pkgs; + modules = [ + { + name = "Basic development shell"; + dotenv.disableHint = true; + + languages = with pkgs; { + nix.enable = true; + nix.lsp.package = nixd; + terraform.enable = true; + opentofu.enable = true; + python.enable = true; + python.package = python313Full; + python.venv.enable = true; + }; + + packages = + with pkgs; + [ + biome + terraformer + terraforming + tflint + terraform-providers.grafana + ] + ++ lib.optionals stdenv.isDarwin ( + with darwin.apple_sdk.frameworks; + [ + Cocoa + CoreFoundation + CoreServices + Security + ] + ); + + enterShell = '' + ${setupScript}/bin/setup-env + ''; + } + ]; + }; + } + ); + }; +} \ No newline at end of file From f8642cca461b7443de3c1e96b4bb73c9eeda10c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:31:02 +0100 Subject: [PATCH 10/14] formatting --- README.md | 8 ++++---- run.py | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8bb1be7..c01e4df 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ Before starting the script you need to enter your grafana & ldap credentials in path to your .csv file containing the bindings. ### Binding -To bind LDAP-groups to grafana-teams and grant these teams access to folders you need to provide a .csv file. Please note -that the first row of the csv is recognized as a header-row and is therefore being ignored. -The file needs to contain the following information in this exact order: +To bind LDAP-groups to grafana-teams and grant these teams access to folders you need to provide a .csv file. Please note +that the first row of the csv is recognized as a header-row and is therefore being ignored. +The file needs to contain the following information in this exact order: * **LDAP-Group**: The LDAP group which will be used for mapping. * **Grafana-Team Name**: The name of the Grafana team which will be created (if not exist) and where the group's users will be added to. * **Grafana-Team ID**: The ID of the Grafana team (currently not used). @@ -65,7 +65,7 @@ Using this CSV mapping will result in the following operations: #### Removing Bindings When a binding is removed in your .csv-file, this binding is also removed by the script. So if there is a team in your grafana instance which -is not defined by the current binding the team will be deleted. This also applies to users. **This does not apply to folders! +is not defined by the current binding the team will be deleted. This also applies to users. **This does not apply to folders! Folders need to be deleted manually if not needed anymore!** diff --git a/run.py b/run.py index 46fd4bf..ce668f1 100644 --- a/run.py +++ b/run.py @@ -2,6 +2,8 @@ import logging import sys +from script.core import startUserSync + class DispatchingFormatter: def __init__(self, formatters, default_formatter): self._formatters = formatters From 44a55b627d4e6d248e2865bf88696b515387bf4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:31:31 +0100 Subject: [PATCH 11/14] open file with 'with' keyword --- script/config.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/script/config.py b/script/config.py index f7060fc..0bbaa14 100644 --- a/script/config.py +++ b/script/config.py @@ -42,10 +42,13 @@ def load_config(self, config_path): Loads the config_mock.yml file present in the directory and fills all global variables with the defined config. """ try: - config = yaml.safe_load(open(config_path))["config"] + with open(config_path) as f: + config = yaml.safe_load(f)["config"] except FileNotFoundError as e: - logger.error("Config-file %s does not exist!", config_path) + logger.error("Config file %s does not exist!", config_path) raise e + except Exception as e: + logger.error("Error reading config file %s: %s", config_path, str(e)) grafana_config = config.get("grafana", {}) self.GRAFANA_AUTH = ( From c7f9f9b645149be32c1cc292faa1aefd538bddde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:31:48 +0100 Subject: [PATCH 12/14] formatting --- script/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/core.py b/script/core.py index f4fcb62..50d0b05 100644 --- a/script/core.py +++ b/script/core.py @@ -250,11 +250,11 @@ def startUserSync(config_path, bind, dry_run): logger.info("Starting user synchronization...") configuration = config(config_path) - + configuration.DRY_RUN = dry_run if configuration.DRY_RUN: logger.info("!! DryRun enabled: Changes will not be applied !!") - + logger.info("=================================================") logger.info("Setting up the connection to the Grafana server..") From 6c4541a659461756af6d2b63d8d57bdebdc62901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:32:31 +0100 Subject: [PATCH 13/14] removed unused filter, fixed login, name and email being arrays and thus crashing the script --- script/ldap.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/script/ldap.py b/script/ldap.py index aa1b08e..75253b1 100644 --- a/script/ldap.py +++ b/script/ldap.py @@ -73,11 +73,6 @@ def fetch_users_of_group(group_name): for group in groups: for member_dn in group["attributes"][configuration.LDAP_MEMBER_ATTRIBUTE]: - if configuration.LDAP_USER_SEARCH_FILTER: - user_query_filter = configuration.LDAP_USER_SEARCH_FILTER - else: - user_query_filter = "(objectClass=*)" - user = get_member(member_dn) if not user: @@ -96,8 +91,7 @@ def fetch_users_of_group(group_name): # Filter out any duplicates from the result array # Also we remove the object class key - distinct_results = list({u["login"]: {i:u[i] for i in u if i != "objectClass"} for u in result}.values()) - return distinct_results + return list({u["login"]: {i:u[i] for i in u if i != "objectClass"} for u in result}.values()) def get_users_of_group(group): @@ -167,8 +161,8 @@ def fetch_member(member_dn): connection.unbind() return { - "login": login, - "name": name, - "email": mail, + "login": login[0] if isinstance(login, list) else login, + "name": name[0] if isinstance(name, list) else name, + "email": mail[0] if isinstance(mail, list) else mail, "objectClass": objectClass } From 4c2dbc902b73b9ab3799ba19f643ccfe3f33519e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pl=C3=BCddemann?= Date: Mon, 28 Oct 2024 17:47:08 +0100 Subject: [PATCH 14/14] updated apiVersion of cronjob --- .../grafana-ldap-sync/templates/cronjob.yaml | 2 +- deploy/helm/grafana-ldap-sync/values.yaml | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml index 48fc2f9..72a7e6e 100644 --- a/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml +++ b/deploy/helm/grafana-ldap-sync/templates/cronjob.yaml @@ -1,4 +1,4 @@ -apiVersion: batch/v1beta1 +apiVersion: batch/v1 kind: CronJob metadata: name: {{ include "grafana-ldap-sync.fullname" . }} diff --git a/deploy/helm/grafana-ldap-sync/values.yaml b/deploy/helm/grafana-ldap-sync/values.yaml index 1bb6860..3b22e31 100644 --- a/deploy/helm/grafana-ldap-sync/values.yaml +++ b/deploy/helm/grafana-ldap-sync/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - repository: DOCKERHUB_USER/grafana-ldap-sync-script + repository: ghcr.io/novatecconsulting/grafana-ldap-sync-script pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. tag: "latest" @@ -25,18 +25,21 @@ serviceAccount: podAnnotations: {} -podSecurityContext: {} +podSecurityContext: + {} # fsGroup: 2000 -securityContext: {} +securityContext: + {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true # runAsUser: 1000 - -resources: {} + +resources: + {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following @@ -95,6 +98,8 @@ config: user: admin # Password of account with admin rights on target grafana-server. password: admin + # Grafana Organization ID that should be used to insert the teams. + org_id: 1 ldap: # Set to True if NTLM should be used for LDAP. @@ -128,7 +133,6 @@ config: # Password for the LDAP-Server. password: password - # If you want to set the CSV Content explicitly set the content of csvContent to a string. # If this is blank, then we'll use the `teams` and `folders` maps # Of note, the Grafana-Team-ID value is not used so you can ignore that or set it to 1