Compare commits
8 Commits
feature/co
...
fix/argocd
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d9d01c240 | |||
| b290d39d64 | |||
| c8d3ef4631 | |||
| 46912cff8c | |||
| 1af50c45ff | |||
| 40551094e6 | |||
| 4297c2d8bf | |||
| 07ba717de9 |
@@ -231,12 +231,51 @@ jobs:
|
||||
if: steps.img.outputs.changed != 'true'
|
||||
run: echo "${{ matrix.description }} image tag unchanged; skipping build."
|
||||
|
||||
- name: Set up Buildx
|
||||
- name: Check if image exists on registry
|
||||
if: steps.img.outputs.changed == 'true'
|
||||
id: check_image
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
new_repo="${{ steps.img.outputs.new_repo }}"
|
||||
new_tag="${{ steps.img.outputs.new_tag }}"
|
||||
registry_user="${{ secrets.REGISTRY_USER }}"
|
||||
registry_password="${{ secrets.REGISTRY_PASSWORD }}"
|
||||
|
||||
# Extract registry host and image name
|
||||
registry_host=$(echo "$new_repo" | cut -d/ -f1)
|
||||
image_path=$(echo "$new_repo" | cut -d/ -f2-)
|
||||
|
||||
echo "Checking if $new_repo:$new_tag exists on registry $registry_host"
|
||||
|
||||
# Use Docker Registry API v2 to check manifest
|
||||
# Format: https://registry/v2/{image_path}/manifests/{tag}
|
||||
manifest_url="https://${registry_host}/v2/${image_path}/manifests/${new_tag}"
|
||||
|
||||
# Check with authentication
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-u "${registry_user}:${registry_password}" \
|
||||
-H "Accept: application/vnd.docker.distribution.manifest.v2+json,application/vnd.docker.distribution.manifest.list.v2+json" \
|
||||
"$manifest_url" || echo "000")
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
echo "Image already exists on registry (HTTP $http_code)"
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Image does not exist on registry (HTTP $http_code)"
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Skip if image already exists
|
||||
if: steps.img.outputs.changed == 'true' && steps.check_image.outputs.exists == 'true'
|
||||
run: echo "${{ matrix.description }} image ${{ steps.img.outputs.new_image }} already exists on registry; skipping build."
|
||||
|
||||
- name: Set up Buildx
|
||||
if: steps.img.outputs.changed == 'true' && steps.check_image.outputs.exists == 'false'
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to registry
|
||||
if: steps.img.outputs.changed == 'true'
|
||||
if: steps.img.outputs.changed == 'true' && steps.check_image.outputs.exists == 'false'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ steps.img.outputs.registry }}
|
||||
@@ -244,7 +283,7 @@ jobs:
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and push ${{ matrix.description }} (exact tag from deployment)
|
||||
if: steps.img.outputs.changed == 'true'
|
||||
if: steps.img.outputs.changed == 'true' && steps.check_image.outputs.exists == 'false'
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ${{ matrix.build_context }}
|
||||
|
||||
95
Documentation/ARGOCD-INGRESS-FIX.md
Normal file
95
Documentation/ARGOCD-INGRESS-FIX.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# ArgoCD Ingress "Progressing" State Fix
|
||||
|
||||
## Problem
|
||||
|
||||
The `django` Ingress resource in the `vorgabenui` namespace was stuck in "Progressing" state in ArgoCD and would not transition to "Healthy".
|
||||
|
||||
### Root Cause
|
||||
|
||||
ArgoCD determines Ingress health by checking if the `status.loadBalancer.ingress` field is populated with an IP address or hostname. Without this field, the Ingress is considered "Progressing" indefinitely.
|
||||
|
||||
The issue occurred because **Traefik was not configured to report its IP address** in the Ingress status field.
|
||||
|
||||
## Solution
|
||||
|
||||
Two changes were made to fix this issue:
|
||||
|
||||
### 1. Update Ingress Annotation (Applied)
|
||||
|
||||
**File**: `argocd/ingress.yaml`
|
||||
|
||||
**Change**:
|
||||
```yaml
|
||||
# Before
|
||||
annotations:
|
||||
argocd.argoproj.io/ignore-healthcheck: "true"
|
||||
|
||||
# After
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
- The `ignore-healthcheck` annotation was causing ArgoCD to not monitor the Ingress health at all
|
||||
- The `sync-wave: "1"` annotation ensures the Ingress syncs after the Deployment and Service are ready (which have default sync-wave of 0)
|
||||
- This allows ArgoCD to properly assess the Ingress health status
|
||||
|
||||
### 2. Configure Traefik to Report Ingress Status (Cluster Patch)
|
||||
|
||||
**Patch Command**:
|
||||
```bash
|
||||
kubectl patch deployment traefik -n traefik --type='json' \
|
||||
-p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik"}]'
|
||||
```
|
||||
|
||||
**Configuration Flag Added**:
|
||||
```
|
||||
--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
This flag tells Traefik to:
|
||||
- Watch for changes to Ingress resources in the cluster
|
||||
- Monitor the Service `traefik/traefik` (the Traefik LoadBalancer service)
|
||||
- Automatically populate `status.loadBalancer.ingress[].ip` with the service's external IP address
|
||||
- Allow ArgoCD to detect when the Ingress has been assigned an IP and transition to "Healthy"
|
||||
|
||||
## Result
|
||||
|
||||
✅ **Status**: RESOLVED
|
||||
|
||||
**Current State**:
|
||||
- Ingress Address: `192.168.17.53` (Traefik LoadBalancer IP)
|
||||
- Ingress Health: Healthy
|
||||
- ArgoCD Application Health: Healthy
|
||||
- Accessible at: `http://vorgabenportal.knowyoursecurity.com/`
|
||||
|
||||
## Verification
|
||||
|
||||
To verify the fix is working:
|
||||
|
||||
```bash
|
||||
# Check Ingress status
|
||||
kubectl get ingress django -n vorgabenui -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
|
||||
# Should output: 192.168.17.53
|
||||
|
||||
# Check ArgoCD application health
|
||||
kubectl get application vorgabenui -n argocd -o jsonpath='{.status.health.status}'
|
||||
# Should output: Healthy
|
||||
|
||||
# Check Traefik configuration
|
||||
kubectl get deploy traefik -n traefik -o jsonpath='{.spec.template.spec.containers[0].args}' | jq 'map(select(. | contains("publishedservice")))'
|
||||
# Should output the publishedservice flag
|
||||
```
|
||||
|
||||
## Documentation Location
|
||||
|
||||
The Traefik configuration patch is documented in:
|
||||
- `argocd/traefik-middleware.yaml` - ConfigMap with patch details and rationale
|
||||
|
||||
## Notes for Future Maintenance
|
||||
|
||||
- If Traefik is upgraded or redeployed via Helm, ensure the `--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik` flag is preserved
|
||||
- The flag must point to the correct LoadBalancer Service that has an external IP
|
||||
- In this case, it's `traefik/traefik` (namespace/service-name) with external IP `192.168.17.53`
|
||||
- If the Traefik service configuration changes, this flag may need adjustment
|
||||
544
Documentation/modelle.md
Normal file
544
Documentation/modelle.md
Normal file
@@ -0,0 +1,544 @@
|
||||
# Alle Modelle der vgui-cicd Django-Anwendung
|
||||
|
||||
Dieses Dokument beschreibt alle Datenmodelle in der vgui-cicd Anwendung mit ihren Eigenschaften, Beziehungen und Verwendungszwecken.
|
||||
|
||||
---
|
||||
|
||||
## App: dokumente
|
||||
|
||||
Die Hauptmodelle für die Verwaltung von Dokumenten, Vorgaben und deren Metadaten.
|
||||
|
||||
### Dokumententyp
|
||||
|
||||
**Zweck**: Kategorisierung von Dokumenten (z. B. Richtlinie, Standard).
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `name` (CharField, max_length=100, **PRIMARY KEY**)
|
||||
- `verantwortliche_ve` (CharField, max_length=255): Die verantwortliche Verwaltungseinheit
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Namen zurück
|
||||
- Dient als Klassifizierungskategorie für Dokumente
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Dokumententyp"`
|
||||
- `verbose_name_plural = "Dokumententypen"`
|
||||
|
||||
---
|
||||
|
||||
### Person
|
||||
|
||||
**Zweck**: Repräsentiert Personen, die als Autoren, Prüfer oder in anderen Rollen tätig sind.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `name` (CharField, max_length=100, **PRIMARY KEY**)
|
||||
- `funktion` (CharField, max_length=255): Funktionsbezeichnung der Person
|
||||
|
||||
**Beziehungen**:
|
||||
- Many-to-Many mit `Dokument` über `verfasste_dokumente` (Autoren)
|
||||
- Many-to-Many mit `Dokument` über `gepruefte_dokumente` (Prüfer)
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Namen zurück
|
||||
- `ordering = ['name']`: Alphabetische Sortierung
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Personen"`
|
||||
|
||||
---
|
||||
|
||||
### Thema
|
||||
|
||||
**Zweck**: Thematische Einordnung und Kategorisierung von Vorgaben innerhalb von Dokumenten.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `name` (CharField, max_length=100, **PRIMARY KEY**)
|
||||
- `erklaerung` (TextField, blank=True): Optionale Erklärung des Themas
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Namen zurück
|
||||
- Der erste Buchstabe des Themas wird in Vorgabennummern verwendet
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Themen"`
|
||||
|
||||
---
|
||||
|
||||
### Dokument
|
||||
|
||||
**Zweck**: Hauptmodell für ein einzelnes Dokument mit allen zugehörigen Metadaten und Inhalten.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `nummer` (CharField, max_length=50, **PRIMARY KEY**): Eindeutige Dokumentennummer
|
||||
- `dokumententyp` (ForeignKey → Dokumententyp, on_delete=PROTECT): Klassifizierung
|
||||
- `name` (CharField, max_length=255): Dokumenttitel
|
||||
- `autoren` (ManyToManyField → Person, related_name='verfasste_dokumente')
|
||||
- `pruefende` (ManyToManyField → Person, related_name='gepruefte_dokumente')
|
||||
- `gueltigkeit_von` (DateField, null=True, blank=True): Gültig ab Datum
|
||||
- `gueltigkeit_bis` (DateField, null=True, blank=True): Gültig bis Datum
|
||||
- `signatur_cso` (CharField, max_length=255, blank=True): CSO-Signatur
|
||||
- `anhaenge` (TextField, blank=True): Beschreibung von Anhängen
|
||||
- `aktiv` (BooleanField, blank=True): Aktivierungsstatus
|
||||
|
||||
**Beziehungen**:
|
||||
- 1-to-Many mit `Vorgabe` (über related_name='vorgaben')
|
||||
- 1-to-Many mit `Geltungsbereich`
|
||||
- 1-to-Many mit `Einleitung`
|
||||
- 1-to-Many mit `Changelog`
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` formatiert als "nummer – name"
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Dokument"`
|
||||
- `verbose_name_plural = "Dokumente"`
|
||||
|
||||
---
|
||||
|
||||
### Vorgabe
|
||||
|
||||
**Zweck**: Repräsentiert eine einzelne Vorgabe oder Anforderung innerhalb eines Dokuments.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `order` (IntegerField): Sortierreihenfolge für die Darstellung
|
||||
- `nummer` (IntegerField): Nummer innerhalb eines Themas/Dokuments. Muss nicht eindeutig sein (z.B. für geänderte Vorgaben)
|
||||
- `dokument` (ForeignKey → Dokument, on_delete=CASCADE, related_name='vorgaben')
|
||||
- `thema` (ForeignKey → Thema, on_delete=PROTECT): Thematische Einordnung
|
||||
- `titel` (CharField, max_length=255): Titel der Vorgabe
|
||||
- `referenzen` (ManyToManyField → Referenz, blank=True): Verweise auf externe Referenzen
|
||||
- `gueltigkeit_von` (DateField): Gültig ab Datum
|
||||
- `gueltigkeit_bis` (DateField, blank=True, null=True): Gültig bis Datum (offen = unbegrenzt)
|
||||
- `stichworte` (ManyToManyField → Stichwort, blank=True): Tags zur Kategorisierung
|
||||
- `relevanz` (ManyToManyField → Rolle, blank=True): Relevante Rollen
|
||||
|
||||
**Beziehungen**:
|
||||
- Foreign Key zu `Dokument` und `Thema`
|
||||
- Many-to-Many zu `Referenz`, `Stichwort`, `Rolle`
|
||||
- 1-to-Many zu `VorgabeLangtext`, `VorgabeKurztext`
|
||||
- 1-to-Many zu `Checklistenfrage`
|
||||
|
||||
**Wichtige Methoden**:
|
||||
|
||||
- `Vorgabennummer()` → str
|
||||
- Generiert eine eindeutige, lesbare Kennummer
|
||||
- Format: "{dokument.nummer}.{thema.name[0]}.{nummer}"
|
||||
- Beispiel: "R0066.A.1"
|
||||
|
||||
- `get_status(check_date=None, verbose=False)` → str
|
||||
- Bestimmt den Status einer Vorgabe zu einem gegebenen Datum
|
||||
- Parameter: `check_date` (Default: heute), `verbose` (Deutsche Beschreibung ja/nein)
|
||||
- Rückgabewerte:
|
||||
- "future": Vorgabe ist noch nicht gültig
|
||||
- "active": Vorgabe ist aktuell gültig
|
||||
- "expired": Vorgabe ist nicht mehr gültig
|
||||
- Verbose-Ausgaben enthalten Datumsangaben
|
||||
|
||||
- `sanity_check_vorgaben()` (statisch) → list
|
||||
- Findet zeitliche Konflikte zwischen Vorgaben mit gleicher Nummer/Thema/Dokument
|
||||
- Überprüft, ob sich Geltungszeiträume überschneiden
|
||||
- Gibt Liste mit Konflikt-Dictionaries zurück
|
||||
|
||||
- `clean()`
|
||||
- Validiert die Vorgabe vor dem Speichern
|
||||
- Ruft `find_conflicts()` auf
|
||||
- Wirft `ValidationError` bei erkannten Konflikten
|
||||
|
||||
- `find_conflicts()` → list
|
||||
- Findet Konflikte mit bestehenden Vorgaben (ausgenommen self)
|
||||
- Überprüft auf zeitliche Überschneidungen
|
||||
- Gibt Liste mit Konflikt-Details zurück
|
||||
|
||||
- `_date_ranges_intersect(start1, end1, start2, end2)` (statisch) → bool
|
||||
- Prüft, ob zwei Datumsbereiche sich überschneiden
|
||||
- `None` als Enddatum = unbegrenzter Bereich
|
||||
- Gibt `True` bei Überschneidung zurück
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt "Vorgabennummer: titel" zurück
|
||||
- Validierung von Gültigkeitszeiträumen ist implementiert
|
||||
- Sehr wichtiges Modell im Geschäftslogik-Kontext
|
||||
|
||||
**Meta**:
|
||||
- `ordering = ['order']`
|
||||
- `verbose_name_plural = "Vorgaben"`
|
||||
|
||||
---
|
||||
|
||||
### VorgabeLangtext
|
||||
|
||||
**Zweck**: Speichert ausführliche Textinhalte (Langtext) einer Vorgabe.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `abschnitt` (ForeignKey → Vorgabe, on_delete=CASCADE): Referenz zur Vorgabe
|
||||
- Erbt von `Textabschnitt` (siehe App: abschnitte):
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Langtext-Abschnitt"`
|
||||
- `verbose_name_plural = "Langtext"`
|
||||
|
||||
---
|
||||
|
||||
### VorgabeKurztext
|
||||
|
||||
**Zweck**: Speichert kurze Textinhalte (Kurztext) einer Vorgabe.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `abschnitt` (ForeignKey → Vorgabe, on_delete=CASCADE): Referenz zur Vorgabe
|
||||
- Erbt von `Textabschnitt` (siehe App: abschnitte):
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Kurztext-Abschnitt"`
|
||||
- `verbose_name_plural = "Kurztext"`
|
||||
|
||||
---
|
||||
|
||||
### Geltungsbereich
|
||||
|
||||
**Zweck**: Speichert den Geltungsbereich-Abschnitt eines Dokuments.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `geltungsbereich` (ForeignKey → Dokument, on_delete=CASCADE): Referenz zum Dokument
|
||||
- Erbt von `Textabschnitt` (siehe App: abschnitte):
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Geltungsbereichs-Abschnitt"`
|
||||
- `verbose_name_plural = "Geltungsbereich"`
|
||||
|
||||
---
|
||||
|
||||
### Einleitung
|
||||
|
||||
**Zweck**: Speichert die Einleitungs-Abschnitte eines Dokuments.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `einleitung` (ForeignKey → Dokument, on_delete=CASCADE): Referenz zum Dokument
|
||||
- Erbt von `Textabschnitt` (siehe App: abschnitte):
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Einleitungs-Abschnitt"`
|
||||
- `verbose_name_plural = "Einleitung"`
|
||||
|
||||
---
|
||||
|
||||
### Checklistenfrage
|
||||
|
||||
**Zweck**: Repräsentiert eine Frage für die Checkliste zu einer Vorgabe.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `vorgabe` (ForeignKey → Vorgabe, on_delete=CASCADE, related_name='checklistenfragen')
|
||||
- `frage` (CharField, max_length=255): Text der Checklistenfrage
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Fragetext zurück
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Frage für Checkliste"`
|
||||
- `verbose_name_plural = "Fragen für Checkliste"`
|
||||
|
||||
---
|
||||
|
||||
### VorgabenTable
|
||||
|
||||
**Zweck**: Proxy-Modell für `Vorgabe` für die Darstellung von Vorgaben in Tabellenform.
|
||||
|
||||
**Besonderheiten**:
|
||||
- Proxy-Modell (kein eigenes Datenbankschema)
|
||||
- Ermöglicht alternative Django-Admin-Ansicht
|
||||
- Erbt alle Felder und Methoden von `Vorgabe`
|
||||
|
||||
**Meta**:
|
||||
- `proxy = True`
|
||||
- `verbose_name = "Vorgabe (Tabellenansicht)"`
|
||||
- `verbose_name_plural = "Vorgaben (Tabellenansicht)"`
|
||||
|
||||
---
|
||||
|
||||
### Changelog
|
||||
|
||||
**Zweck**: Dokumentiert Änderungen und Versionshistorie für Dokumente.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `dokument` (ForeignKey → Dokument, on_delete=CASCADE, related_name='changelog'): Referenz zum Dokument
|
||||
- `autoren` (ManyToManyField → Person): Personen, die die Änderung vorgenommen haben
|
||||
- `datum` (DateField): Datum der Änderung
|
||||
- `aenderung` (TextField): Beschreibung der Änderung
|
||||
|
||||
**Beziehungen**:
|
||||
- Foreign Key zu `Dokument`
|
||||
- Many-to-Many zu `Person`
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` formatiert als "datum – dokumentnummer"
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Changelog-Eintrag"`
|
||||
- `verbose_name_plural = "Changelog"`
|
||||
|
||||
---
|
||||
|
||||
## App: abschnitte
|
||||
|
||||
Modelle für die Verwaltung von Textabschnitten, die von mehreren Modellen geerbt werden.
|
||||
|
||||
### AbschnittTyp
|
||||
|
||||
**Zweck**: Klassifizierung von Textabschnitten (z. B. "Beschreibung", "Erklärung", "Anleitung").
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `abschnitttyp` (CharField, max_length=100, **PRIMARY KEY**): Name des Abschnitttyps
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Namen zurück
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Abschnitttypen"`
|
||||
|
||||
---
|
||||
|
||||
### Textabschnitt (abstrakt)
|
||||
|
||||
**Zweck**: Abstrakte Basisklasse für Textinhalte, die mit anderen Modellen verknüpft sind.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, on_delete=PROTECT, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True): Der Textinhalt
|
||||
- `order` (PositiveIntegerField, default=0): Sortierreihenfolge
|
||||
|
||||
**Besonderheiten**:
|
||||
- Abstrakte Klasse (wird nicht direkt in der Datenbank gespeichert)
|
||||
- Wird von anderen Modellen geerbt: `VorgabeLangtext`, `VorgabeKurztext`, `Geltungsbereich`, `Einleitung`, `Referenzerklaerung`, `Stichworterklaerung`, `RollenBeschreibung`
|
||||
|
||||
**Meta**:
|
||||
- `abstract = True`
|
||||
- `verbose_name = "Abschnitt"`
|
||||
- `verbose_name_plural = "Abschnitte"`
|
||||
|
||||
---
|
||||
|
||||
## App: referenzen
|
||||
|
||||
Modelle für die Verwaltung von Referenzen und Verweisen auf externe Standards.
|
||||
|
||||
### Referenz (MPTT-Tree)
|
||||
|
||||
**Zweck**: Hierarchische Verwaltung von Referenzen und externen Normen (z. B. ISO-Standards, Gesetze, übergeordnete Vorgaben).
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `id` (AutoField, **PRIMARY KEY**)
|
||||
- `name_nummer` (CharField, max_length=100): Nummer/Kennung der Referenz (z. B. "ISO 27001")
|
||||
- `name_text` (CharField, max_length=255, blank=True): Ausführlicher Name/Beschreibung
|
||||
- `oberreferenz` (TreeForeignKey zu self, optional): Parent-Referenz für Hierarchien
|
||||
- `url` (URLField, blank=True): Link zur Referenz
|
||||
|
||||
**Beziehungen**:
|
||||
- Many-to-Many mit `Vorgabe`
|
||||
- MPTT Tree-Struktur für hierarchische Referenzen
|
||||
|
||||
**Wichtige Methoden**:
|
||||
|
||||
- `Path()` → str
|
||||
- Gibt die vollständige Pfad-Hierarchie als String zurück
|
||||
- Format: "Referenz → Subreferenz → Unterreferenz (Beschreibung)"
|
||||
- Beispiel: "ISO → 27000 → 27001 (Information Security Management)"
|
||||
|
||||
**Besonderheiten**:
|
||||
- Verwendet MPPT (Modified Preorder Tree Traversal) für Baumoperationen
|
||||
- `get_ancestors(include_self=True)`: Gibt alle Vorfahren zurück
|
||||
- `unterreferenzen`: Related_name für Kindreferenzen
|
||||
- Sortierung: Nach `name_nummer`
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Referenzen"`
|
||||
- **MPTTMeta**:
|
||||
- `parent_attr = 'oberreferenz'`
|
||||
- `order_insertion_by = ['name_nummer']`
|
||||
|
||||
---
|
||||
|
||||
### Referenzerklaerung
|
||||
|
||||
**Zweck**: Speichert Erklärungen und zusätzliche Informationen zu einer Referenz.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `erklaerung` (ForeignKey → Referenz, on_delete=CASCADE): Referenz zur Referenz
|
||||
- Erbt von `Textabschnitt`:
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Erklärung"`
|
||||
- `verbose_name_plural = "Erklärungen"`
|
||||
|
||||
---
|
||||
|
||||
## App: stichworte
|
||||
|
||||
Modelle für die Verwaltung von Stichworte und Tags.
|
||||
|
||||
### Stichwort
|
||||
|
||||
**Zweck**: Einfache Tag/Keyword-Modell zur Kategorisierung von Vorgaben.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `stichwort` (CharField, max_length=50, **PRIMARY KEY**): Das Stichwort
|
||||
|
||||
**Beziehungen**:
|
||||
- Many-to-Many mit `Vorgabe`
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt das Stichwort zurück
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Stichworte"`
|
||||
|
||||
---
|
||||
|
||||
### Stichworterklaerung
|
||||
|
||||
**Zweck**: Speichert Erklärungen zu Stichworten.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `erklaerung` (ForeignKey → Stichwort, on_delete=CASCADE): Referenz zum Stichwort
|
||||
- Erbt von `Textabschnitt`:
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Erklärung"`
|
||||
- `verbose_name_plural = "Erklärungen"`
|
||||
|
||||
---
|
||||
|
||||
## App: rollen
|
||||
|
||||
Modelle für die Verwaltung von Rollen und deren Beschreibungen.
|
||||
|
||||
### Rolle
|
||||
|
||||
**Zweck**: Definiert Rollen/Positionen im Unternehmen (z. B. "Geschäftsleiter", "IT-Sicherheit", "Datenschutzbeauftragter").
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `name` (CharField, max_length=100, **PRIMARY KEY**): Name der Rolle
|
||||
|
||||
**Beziehungen**:
|
||||
- Many-to-Many mit `Vorgabe` (über `relevanz`)
|
||||
|
||||
**Besonderheiten**:
|
||||
- `__str__()` gibt den Namen zurück
|
||||
- Wird verwendet, um Rollen zu markieren, die von einer Vorgabe betroffen sind
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name_plural = "Rollen"`
|
||||
|
||||
---
|
||||
|
||||
### RollenBeschreibung
|
||||
|
||||
**Zweck**: Speichert detaillierte Beschreibungen und Informationen zu einer Rolle.
|
||||
|
||||
**Wichtige Felder**:
|
||||
- `abschnitt` (ForeignKey → Rolle, on_delete=CASCADE): Referenz zur Rolle
|
||||
- Erbt von `Textabschnitt`:
|
||||
- `abschnitttyp` (ForeignKey → AbschnittTyp, optional)
|
||||
- `inhalt` (TextField, blank=True, null=True)
|
||||
- `order` (PositiveIntegerField, default=0)
|
||||
|
||||
**Meta**:
|
||||
- `verbose_name = "Rollenbeschreibungs-Abschnitt"`
|
||||
- `verbose_name_plural = "Rollenbeschreibung"`
|
||||
|
||||
---
|
||||
|
||||
## Allgemeine Hinweise zur Modellverwaltung
|
||||
|
||||
### Primärschlüssel-Strategie
|
||||
- Viele Modelle verwenden CharField-basierte Primärschlüssel (`name`, `nummer`, `stichwort`)
|
||||
- Dies ermöglicht direkte Verwendung von Strings als Identifikatoren
|
||||
- Vorteil: Lesbarkeit; Nachteil: Umbenennungen sind kritisch
|
||||
|
||||
### On-Delete-Strategien
|
||||
- **PROTECT**: Verwendet für wichtige Beziehungen (z. B. Dokumententyp, Thema, AbschnittTyp)
|
||||
- Verhindert versehentliches Löschen von Daten, auf die verwiesen wird
|
||||
- **CASCADE**: Verwendet für Unterkomponenten (z. B. Vorgabe → Dokument)
|
||||
- Löscht abhängige Datensätze automatisch
|
||||
- **SET_NULL**: Nur bei optionalen Referenzen (z. B. Oberreferenz in Referenz-Tree)
|
||||
|
||||
### Validierungsmechanismen
|
||||
- **Vorgabe.clean()**: Validiert Gültigkeitszeiträume
|
||||
- **Vorgabe.find_conflicts()**: Prüft zeitliche Überschneidungen
|
||||
- Wird von Django-Admin automatisch aufgerufen vor dem Speichern
|
||||
|
||||
### MPTT (Modified Preorder Tree Traversal)
|
||||
- Verwendet in `Referenz` für hierarchische Strukturen
|
||||
- Ermöglicht effiziente Abfragen von Vorfahren und Nachkommen
|
||||
- Zusätzliche Datenbank-Felder für Tree-Management (automatisch verwaltet)
|
||||
|
||||
### Textabschnitt-Vererbung
|
||||
- Mehrere Modelle erben von `Textabschnitt`
|
||||
- Wird verwendet für Lang-/Kurztexte, Erklärungen, Beschreibungen
|
||||
- `order`-Feld ermöglicht Sortierung mehrerer Abschnitte
|
||||
|
||||
### Datumsverwaltung
|
||||
- `gueltigkeit_von`: Immer erforderlich für Vorgaben
|
||||
- `gueltigkeit_bis`: Optional; `None` bedeutet unbegrenzte Gültigkeit
|
||||
- `_date_ranges_intersect()` prüft korrekt auf Überschneidungen mit None-Werten
|
||||
|
||||
### Many-to-Many-Beziehungen
|
||||
- Vielfach verwendet für flexible Zuordnungen (Autoren, Stichworte, Rollen, Referenzen)
|
||||
- `related_name`-Attribute ermöglichen rückwärts Zugriff
|
||||
- Beispiel: `dokument.vorgaben.all()`, `person.verfasste_dokumente.all()`
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung der Beziehungen
|
||||
|
||||
```
|
||||
Dokumententyp ← Dokument
|
||||
Person ← Dokument (Autoren/Prüfer)
|
||||
Dokument → Vorgabe (1-to-Many)
|
||||
Dokument → Geltungsbereich (1-to-Many)
|
||||
Dokument → Einleitung (1-to-Many)
|
||||
Dokument → Changelog (1-to-Many)
|
||||
|
||||
Thema ← Vorgabe
|
||||
Vorgabe → VorgabeLangtext (1-to-Many)
|
||||
Vorgabe → VorgabeKurztext (1-to-Many)
|
||||
Vorgabe → Checklistenfrage (1-to-Many)
|
||||
Vorgabe ← Referenz (Many-to-Many)
|
||||
Vorgabe ← Stichwort (Many-to-Many)
|
||||
Vorgabe ← Rolle (Many-to-Many)
|
||||
|
||||
Referenz → Referenz (Hierarchie via MPPT)
|
||||
Referenz → Referenzerklaerung (1-to-Many)
|
||||
|
||||
Stichwort → Stichworterklaerung (1-to-Many)
|
||||
|
||||
Rolle → RollenBeschreibung (1-to-Many)
|
||||
|
||||
AbschnittTyp ← Textabschnitt (von verschiedenen Modellen geerbt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entwicklungsrichtlinien
|
||||
|
||||
- Alle Modelle sollten aussagekräftige `__str__()`-Methoden haben
|
||||
- `verbose_name` und `verbose_name_plural` sollten auf Deutsch sein (für Django-Admin)
|
||||
- Validierungslogik (z. B. `clean()`) sollte implementiert werden für komplexe Business-Logic
|
||||
- Related-Names sollten aussagekräftig und konsistent sein
|
||||
- Textinhalte sollten die `Textabschnitt`-Basisklasse erben
|
||||
- Datumsverwaltung: Immer auf None-Werte bei `gueltigkeit_bis` achten, wenn Vorgaben noch aktiv sind.
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
mountPath: /data
|
||||
containers:
|
||||
- name: web
|
||||
image: git.baumann.gr/adebaumann/vui:0.960
|
||||
image: git.baumann.gr/adebaumann/vui:0.961
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
|
||||
@@ -4,7 +4,7 @@ metadata:
|
||||
name: django
|
||||
namespace: vorgabenui
|
||||
annotations:
|
||||
argocd.argoproj.io/ignore-healthcheck: "true"
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
|
||||
24
argocd/traefik-middleware.yaml
Normal file
24
argocd/traefik-middleware.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
# Traefik configuration to enable Ingress status updates
|
||||
# This patch configures Traefik to report its IP address in Ingress.status.loadBalancer
|
||||
# which is required for ArgoCD to properly assess Ingress health status
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: traefik-patch-note
|
||||
namespace: traefik
|
||||
annotations:
|
||||
description: "Manual patch applied to traefik deployment to enable ingress status reporting"
|
||||
data:
|
||||
patch-command: |
|
||||
kubectl patch deployment traefik -n traefik --type='json' \
|
||||
-p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--providers.kubernetesingress.ingressendpoint.publishedservice=traefik/traefik"}]'
|
||||
|
||||
rationale: |
|
||||
The Ingress resource needs its status.loadBalancer.ingress field populated for ArgoCD to assess health.
|
||||
Without this, Ingress resources remain in "Progressing" state indefinitely.
|
||||
|
||||
This flag tells Traefik to:
|
||||
- Monitor the specified Service (traefik/traefik - the LoadBalancer service)
|
||||
- Automatically update Ingress.status.loadBalancer with the service's external IP
|
||||
- Allow ArgoCD to transition the Ingress from "Progressing" to "Healthy"
|
||||
@@ -48,7 +48,7 @@
|
||||
<div class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" style="text-decoration: none; color: #000; display: flex; align-items: center;">
|
||||
<span style="font-size: 24px; margin-right: 8px;">👤</span>
|
||||
<span class="hidden-xs" style="margin-left: 0;">{{ user.username }}</span>
|
||||
<span class="hidden-xs" style="margin-left: 0;">{{ user.first_name }} {{ user.last_name }}</span>
|
||||
<span class="caret" style="margin-left: 8px;"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
@@ -215,7 +215,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-6 text-right">
|
||||
<p class="text-muted">Version {{ version|default:"0.960" }}</p>
|
||||
<p class="text-muted">Version {{ version|default:"0.961" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user