From 59ff83a8f86d6540755c8503377bb6f2f273c08d Mon Sep 17 00:00:00 2001 From: evdo Date: Wed, 16 Apr 2025 15:50:48 +0200 Subject: [PATCH] Added backstage template for basic python django application --- .../entities/catalog-info.yaml | 1 + .../entities/django_template/README.md | 0 .../skeleton/.github/workflows/ci.yaml | 51 ++++++++ .../django_template/skeleton/Dockerfile | 10 ++ .../skeleton/catalog-info.yaml | 35 +++++ .../django_template/skeleton/k8s/django.yml | 78 +++++++++++ .../skeleton/project/manage.py | 22 ++++ .../skeleton/project/project/__init__.py | 0 .../skeleton/project/project/asgi.py | 16 +++ .../skeleton/project/project/settings.py | 123 ++++++++++++++++++ .../skeleton/project/project/urls.py | 17 +++ .../skeleton/project/project/wsgi.py | 16 +++ .../django_template/skeleton/requirements.txt | 4 + .../entities/django_template/template.yaml | 80 ++++++++++++ 14 files changed, 453 insertions(+) create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/README.md create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/.github/workflows/ci.yaml create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/Dockerfile create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/catalog-info.yaml create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/k8s/django.yml create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/manage.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/__init__.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/asgi.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/settings.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/urls.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/wsgi.py create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/requirements.txt create mode 100644 template/stacks/ref-implementation/backstage-templates/entities/django_template/template.yaml diff --git a/template/stacks/ref-implementation/backstage-templates/entities/catalog-info.yaml b/template/stacks/ref-implementation/backstage-templates/entities/catalog-info.yaml index b6f40c3..372abb0 100644 --- a/template/stacks/ref-implementation/backstage-templates/entities/catalog-info.yaml +++ b/template/stacks/ref-implementation/backstage-templates/entities/catalog-info.yaml @@ -10,6 +10,7 @@ spec: - ./app-with-bucket/template.yaml - ./demo-go-hello-world/template.yaml - ./spring-petclinic/template.yaml + - ./django_template/template.yaml --- apiVersion: backstage.io/v1alpha1 kind: Location diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/README.md b/template/stacks/ref-implementation/backstage-templates/entities/django_template/README.md new file mode 100644 index 0000000..e69de29 diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/.github/workflows/ci.yaml b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/.github/workflows/ci.yaml new file mode 100644 index 0000000..5903794 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/.github/workflows/ci.yaml @@ -0,0 +1,51 @@ +name: ci + +on: push + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - + name: Repository meta + id: repository + run: | + registry=${{ github.server_url }} + registry=${registry##http*://} + echo "registry=${registry}" >> "$GITHUB_OUTPUT" + echo "registry=${registry}" + repository="$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')" + echo "repository=${repository}" >> "$GITHUB_OUTPUT" + echo "repository=${repository}" + - + name: Docker meta + uses: docker/metadata-action@v5 + id: docker + with: + images: ${{ steps.repository.outputs.registry }}/${{ steps.repository.outputs.repository }} + - + name: Login to registry + uses: docker/login-action@v3 + with: + registry: {% raw %} ${{ steps.repository.outputs.registry }} + username: ${{ secrets.PACKAGES_USER }} + password: ${{ secrets.PACKAGES_TOKEN }} {% endraw %} + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + buildkitd-flags: '--allow-insecure-entitlement network.host' + driver-opts: network=host + - + name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + allow: network.host + network: host + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.docker.outputs.tags }} diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/Dockerfile b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/Dockerfile new file mode 100644 index 0000000..cfcf4ea --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["python", "project/manage.py", "runserver", "0.0.0.0:8000"] \ No newline at end of file diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/catalog-info.yaml b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/catalog-info.yaml new file mode 100644 index 0000000..5d46ffc --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/catalog-info.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: ${{ values.name }} + description: A Django application generated via a custom Backstage template. + annotations: + backstage.io/techdocs-ref: dir:. + backstage.io/kubernetes-label-selector: 'app=django' + backstage.io/kubernetes-namespace: ${{ values.namespace }} + argocd/app-name: ${{values.name | dump}} + links: + - url: https://gitea-192-168-197-3.c-one-infra.de/giteaAdmin/${{ values.name }} + title: Source Code Repository + icon: git +spec: + owner: guests + lifecycle: experimental + type: service + system: ${{ values.name | dump }} +--- +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: ${{ values.name | dump }} + description: A system containing a Django application created via template. + annotations: + backstage.io/techdocs-ref: dir:. + links: + - url: https://gitea-192-168-197-3.c-one-infra.de/giteaAdmin/${{ values.name }} + title: Gitea Repository + icon: git +spec: + owner: guests + lifecycle: experimental diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/k8s/django.yml b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/k8s/django.yml new file mode 100644 index 0000000..a879fc9 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/k8s/django.yml @@ -0,0 +1,78 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: ${{ values.namespace }} +--- +apiVersion: v1 +kind: Service +metadata: + name: django + namespace: ${{ values.namespace }} + labels: + app: django +spec: + type: ClusterIP + ports: + - port: 8000 + targetPort: http + name: http + selector: + app: django + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ${{ values.namespace }} + namespace: ${{ values.namespace }} + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + ingressClassName: nginx + rules: + - host: 192-168-197-3.c-one-infra.de + http: + paths: + - backend: + service: + name: django + port: + number: 8000 + path: /${{ values.name }} + pathType: Prefix + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: django + namespace: ${{ values.namespace }} + labels: + app: django +spec: + replicas: 1 + selector: + matchLabels: + app: django + template: + metadata: + labels: + app: django + spec: + containers: + - name: django + image: gitea-192-168-197-3.c-one-infra.de/giteaadmin/${{ values.name }} + command: ["python", "project/manage.py", "runserver", "0.0.0.0:8000"] + env: + - name: APP_NAME + value: ${{ values.name }} + ports: + - name: http + containerPort: 8000 + livenessProbe: + httpGet: + path: /healthcheck + port: http + periodSeconds: 10 + diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/manage.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/manage.py new file mode 100644 index 0000000..2c49f3a --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/__init__.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/asgi.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/asgi.py new file mode 100644 index 0000000..3d66fe3 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for project project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') + +application = get_asgi_application() diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/settings.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/settings.py new file mode 100644 index 0000000..6de8f34 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/settings.py @@ -0,0 +1,123 @@ +""" +Django settings for project project. + +Generated by 'django-admin startproject' using Django 5.2. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.2/ref/settings/ +""" + +from pathlib import Path +import os + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-8uqicq3zw$=f=%wc=z*!o)0f-%1f_v^6+^3p4+8fcq!u$3fnq6' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ["*", ] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'project.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'project.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/urls.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/urls.py new file mode 100644 index 0000000..45f163b --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/urls.py @@ -0,0 +1,17 @@ +from django.http import HttpResponse +from django.urls import path + +def hello_world(request): + return HttpResponse("Hello EDP!") + +urlpatterns = [ + path('', hello_world) +] + +def healthcheck(request): + return HttpResponse("I'm healthy!") + +urlpatterns = [ + path("healthcheck/", healthcheck), + path("", hello_world), +] \ No newline at end of file diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/wsgi.py b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/wsgi.py new file mode 100644 index 0000000..e896f59 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/project/project/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for project project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') + +application = get_wsgi_application() diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/requirements.txt b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/requirements.txt new file mode 100644 index 0000000..743ab48 --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/skeleton/requirements.txt @@ -0,0 +1,4 @@ +Django>=4.2 +requests +celery +django-celery-beat \ No newline at end of file diff --git a/template/stacks/ref-implementation/backstage-templates/entities/django_template/template.yaml b/template/stacks/ref-implementation/backstage-templates/entities/django_template/template.yaml new file mode 100644 index 0000000..32370ca --- /dev/null +++ b/template/stacks/ref-implementation/backstage-templates/entities/django_template/template.yaml @@ -0,0 +1,80 @@ +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: django-app + title: Django App Template + description: Template for creating a simple Django application with ArgoCD deployment +spec: + owner: user:guest + type: service + + parameters: + - title: Django App Configuration + required: + - name + properties: + name: + title: Project Name + type: string + description: Unique name of the Django app + ui:autofocus: true + + steps: + - id: fetch-template + name: Generate Project + action: fetch:template + input: + url: ./skeleton + values: + name: ${{ parameters.name }} + gitea_username: ${{ secrets.GITEA_USERNAME }} + gitea_password: ${{ secrets.GITEA_PASSWORD }} + targetPath: ./ + + - id: fetch-overrides # Apply specific overrides to add features and make modifications for compatibility + name: Fetch Overrides + action: fetch:template + input: + # url: ./skeleton/.github/workflows + # targetPath: ./.github/workflows + url: ./skeleton/ + targetPath: ./ + replace: true + values: + name: ${{ parameters.name }} + namespace: ${{ parameters.name }} + + - id: publish + name: Publish to Gitea + action: publish:gitea + input: + repoUrl: gitea-192-168-197-3.c-one-infra.de:443/?repo=${{ parameters.name }} + description: Django app for ${{ parameters.name }} + sourcePath: ./ + defaultBranch: main + + - id: create-argocd-app + name: Create ArgoCD App + action: cnoe:create-argocd-app + input: + appName: ${{ parameters.name }} + appNamespace: ${{ parameters.name }} + argoInstance: in-cluster + projectName: default + repoUrl: https://gitea-192-168-197-3.c-one-infra.de:443/giteaAdmin/${{ parameters.name }} + path: "k8s" + + - id: register + name: Register in Backstage Catalog + action: catalog:register + input: + repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }} + catalogInfoPath: catalog-info.yaml + + output: + links: + - title: Open Repository + url: ${{ steps['publish'].output.remoteUrl }} + - title: View in Catalog + icon: catalog + entityRef: ${{ steps['register'].output.entityRef }}