diff --git a/README.md b/z1/README.md similarity index 100% rename from README.md rename to z1/README.md diff --git a/myproject/Dockerfile b/z1/myproject/Dockerfile similarity index 100% rename from myproject/Dockerfile rename to z1/myproject/Dockerfile diff --git a/myproject/README.md b/z1/myproject/README.md similarity index 100% rename from myproject/README.md rename to z1/myproject/README.md diff --git a/myproject/docker-compose.yml b/z1/myproject/docker-compose.yml similarity index 100% rename from myproject/docker-compose.yml rename to z1/myproject/docker-compose.yml diff --git a/myproject/manage.py b/z1/myproject/manage.py similarity index 100% rename from myproject/manage.py rename to z1/myproject/manage.py diff --git a/myproject/myapp/__init__.py b/z1/myproject/myapp/__init__.py similarity index 100% rename from myproject/myapp/__init__.py rename to z1/myproject/myapp/__init__.py diff --git a/myproject/myapp/admin.py b/z1/myproject/myapp/admin.py similarity index 100% rename from myproject/myapp/admin.py rename to z1/myproject/myapp/admin.py diff --git a/myproject/myapp/apps.py b/z1/myproject/myapp/apps.py similarity index 100% rename from myproject/myapp/apps.py rename to z1/myproject/myapp/apps.py diff --git a/myproject/myapp/forms.py b/z1/myproject/myapp/forms.py similarity index 100% rename from myproject/myapp/forms.py rename to z1/myproject/myapp/forms.py diff --git a/myproject/myapp/migrations/0001_initial.py b/z1/myproject/myapp/migrations/0001_initial.py similarity index 100% rename from myproject/myapp/migrations/0001_initial.py rename to z1/myproject/myapp/migrations/0001_initial.py diff --git a/myproject/myapp/migrations/__init__.py b/z1/myproject/myapp/migrations/__init__.py similarity index 100% rename from myproject/myapp/migrations/__init__.py rename to z1/myproject/myapp/migrations/__init__.py diff --git a/myproject/myapp/models.py b/z1/myproject/myapp/models.py similarity index 100% rename from myproject/myapp/models.py rename to z1/myproject/myapp/models.py diff --git a/myproject/myapp/static/styles.css b/z1/myproject/myapp/static/styles.css similarity index 100% rename from myproject/myapp/static/styles.css rename to z1/myproject/myapp/static/styles.css diff --git a/myproject/myapp/templates/student_form.html b/z1/myproject/myapp/templates/student_form.html similarity index 100% rename from myproject/myapp/templates/student_form.html rename to z1/myproject/myapp/templates/student_form.html diff --git a/myproject/myapp/templates/student_list.html b/z1/myproject/myapp/templates/student_list.html similarity index 100% rename from myproject/myapp/templates/student_list.html rename to z1/myproject/myapp/templates/student_list.html diff --git a/myproject/myapp/tests.py b/z1/myproject/myapp/tests.py similarity index 100% rename from myproject/myapp/tests.py rename to z1/myproject/myapp/tests.py diff --git a/myproject/myapp/urls.py b/z1/myproject/myapp/urls.py similarity index 100% rename from myproject/myapp/urls.py rename to z1/myproject/myapp/urls.py diff --git a/myproject/myapp/views.py b/z1/myproject/myapp/views.py similarity index 100% rename from myproject/myapp/views.py rename to z1/myproject/myapp/views.py diff --git a/myproject/myproject/__init__.py b/z1/myproject/myproject/__init__.py similarity index 100% rename from myproject/myproject/__init__.py rename to z1/myproject/myproject/__init__.py diff --git a/myproject/myproject/__pycache__/__init__.cpython-312.pyc b/z1/myproject/myproject/__pycache__/__init__.cpython-312.pyc similarity index 100% rename from myproject/myproject/__pycache__/__init__.cpython-312.pyc rename to z1/myproject/myproject/__pycache__/__init__.cpython-312.pyc diff --git a/myproject/myproject/__pycache__/settings.cpython-312.pyc b/z1/myproject/myproject/__pycache__/settings.cpython-312.pyc similarity index 100% rename from myproject/myproject/__pycache__/settings.cpython-312.pyc rename to z1/myproject/myproject/__pycache__/settings.cpython-312.pyc diff --git a/myproject/myproject/asgi.py b/z1/myproject/myproject/asgi.py similarity index 100% rename from myproject/myproject/asgi.py rename to z1/myproject/myproject/asgi.py diff --git a/myproject/myproject/settings.py b/z1/myproject/myproject/settings.py similarity index 100% rename from myproject/myproject/settings.py rename to z1/myproject/myproject/settings.py diff --git a/myproject/myproject/urls.py b/z1/myproject/myproject/urls.py similarity index 100% rename from myproject/myproject/urls.py rename to z1/myproject/myproject/urls.py diff --git a/myproject/myproject/wsgi.py b/z1/myproject/myproject/wsgi.py similarity index 100% rename from myproject/myproject/wsgi.py rename to z1/myproject/myproject/wsgi.py diff --git a/myproject/prepare-app.sh b/z1/myproject/prepare-app.sh similarity index 100% rename from myproject/prepare-app.sh rename to z1/myproject/prepare-app.sh diff --git a/myproject/remove-app.sh b/z1/myproject/remove-app.sh similarity index 100% rename from myproject/remove-app.sh rename to z1/myproject/remove-app.sh diff --git a/myproject/requirements.txt b/z1/myproject/requirements.txt similarity index 100% rename from myproject/requirements.txt rename to z1/myproject/requirements.txt diff --git a/myproject/start-app.sh b/z1/myproject/start-app.sh similarity index 100% rename from myproject/start-app.sh rename to z1/myproject/start-app.sh diff --git a/myproject/stop-app.sh b/z1/myproject/stop-app.sh similarity index 100% rename from myproject/stop-app.sh rename to z1/myproject/stop-app.sh diff --git a/myproject/wait-for-it.sh b/z1/myproject/wait-for-it.sh similarity index 100% rename from myproject/wait-for-it.sh rename to z1/myproject/wait-for-it.sh diff --git a/z2/README.md b/z2/README.md new file mode 100644 index 0000000..e69de29 diff --git a/z2/myproject/Dockerfile b/z2/myproject/Dockerfile new file mode 100644 index 0000000..e2c70f9 --- /dev/null +++ b/z2/myproject/Dockerfile @@ -0,0 +1,28 @@ +# Use an official Python runtime as a parent image +FROM python:3.9-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# Set the working directory in the container +WORKDIR /app + +# Copy the requirements file into the container +COPY requirements.txt . + +# Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the current directory contents into the container +COPY . . + +# Copy wait-for-it script and make it executable +COPY wait-for-it.sh /wait-for-it.sh +RUN chmod +x /wait-for-it.sh + +# Expose port 8000 for the Django app +EXPOSE 8000 + +# Run the Django development server, wait for the DB to be ready first +CMD ["/wait-for-it.sh", "db:5432", "--", "python", "manage.py", "runserver", "0.0.0.0:8000"] diff --git a/z2/myproject/README.md b/z2/myproject/README.md new file mode 100644 index 0000000..4808970 --- /dev/null +++ b/z2/myproject/README.md @@ -0,0 +1,102 @@ +Application Overview +This web application is a basic Django project connected to a PostgreSQL database. + +Django web service: Runs a Django development server. +PostgreSQL database service: Stores data for the Django application. +The services are configured to communicate with each other over a custom virtual network, and the database data is persisted in a named volume. + +Project Structure +The repository contains the following key files: + +docker-compose.yml: Defines the services (Django and PostgreSQL) and configurations. +Dockerfile: Defines the configuration for building the Django web service. +wait-for-it.sh: A script that ensures the Django app waits for PostgreSQL to be ready before starting. +requirements.txt: Python dependencies for the Django application. +manage.py: The Django project’s command-line utility. +prepare-app.sh: Script to prepare the application environment (build images, create volumes, and networks). +start-app.sh: Script to start the application (launch containers and configure them to restart on failure). +stop-app.sh: Script to stop the containers (pause the application without resetting the state). +remove-app.sh: Script to remove all traces of the application (cleanup). +Virtual Networks and Named Volumes +Network: mic_default_san +Custom network for communication between the services. +Volume: postgres_data +A named volume used to persist PostgreSQL data so that data is not lost if the container is stopped or removed. +Container Configuration +PostgreSQL Service +Image: postgres:13 +Environment variables: +POSTGRES_DB: Database name (mydatabase) +POSTGRES_USER: Database user (myuser) +POSTGRES_PASSWORD: Database password (mypassword) +Ports: Exposes PostgreSQL on localhost:5432 +Volume: Persists data in the postgres_data volume. +Django Web Service +Build Context: The Dockerfile is used to build the web service container. +Command: Waits for PostgreSQL to be available before starting the Django development server. +Ports: Exposes the Django app on localhost:8000 +Environment Variables: +DATABASE_URL: Connection string for the database. +Instructions for Running the Application +Step 1: Prepare the Application +Run the prepare-app.sh script to build the Docker images, create named volumes, and set up networks. + +bash +Copy +Edit +./prepare-app.sh +Step 2: Start the Application +Run the start-app.sh script to start the application. This will launch the containers, and you can access the web application in your browser. + +bash +Copy +Edit +./start-app.sh +After running the script, you will see the following message: + +arduino +Copy +Edit +The app is available at http://localhost:8000 +Step 3: Open the Web Application +Open a browser and navigate to http://localhost:8000 to view the Django application. +Step 4: Stop the Application +To stop the application without removing containers and volumes, use the stop-app.sh script. This will pause the services but retain the current state. + +bash +Copy +Edit +./stop-app.sh +Step 5: Remove the Application +If you want to completely remove all containers, networks, and volumes created by the application, use the remove-app.sh script. + +bash +Copy +Edit +./remove-app.sh +After running the script, everything related to the application will be removed. + +Example Workflow +Here’s an example of working with the application: + +bash +Copy +Edit +# Prepare the application +./prepare-app.sh +Preparing app ... + +# Run the application +./start-app.sh +Running app ... +The app is available at http://localhost:8000 + +# Open the web application in a browser. + +# Stop the application +./stop-app.sh +Stopping app ... + +# Remove the application +./remove-app.sh +Removed app. diff --git a/z2/myproject/docker-compose.yml b/z2/myproject/docker-compose.yml new file mode 100644 index 0000000..e9948d5 --- /dev/null +++ b/z2/myproject/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.8' + +services: + db: + image: postgres:13 + environment: + POSTGRES_DB: mydatabase + POSTGRES_USER: myuser + POSTGRES_PASSWORD: mypassword + volumes: + - postgres_data:/var/lib/postgresql/data/ + ports: + - "5432:5432" + networks: + - mic_default_san + + web: + build: . + command: /wait-for-it.sh db:5432 -- python manage.py runserver 0.0.0.0:8000 + volumes: + - .:/app + ports: + - "8000:8000" + depends_on: + - db + environment: + DATABASE_URL: postgres://myuser:mypassword@db:5432/mydatabase + networks: + mic_default_san: + +volumes: + postgres_data: + +networks: + mic_default_san: diff --git a/z2/myproject/k8s/deployment.yaml b/z2/myproject/k8s/deployment.yaml new file mode 100644 index 0000000..5036d03 --- /dev/null +++ b/z2/myproject/k8s/deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: django-app + namespace: webapp-namespace +spec: + replicas: 1 + selector: + matchLabels: + app: django + template: + metadata: + labels: + app: django + spec: + containers: + - name: django + image: django-app:latest + imagePullPolicy: Never + ports: + - containerPort: 8000 + env: + - name: POSTGRES_HOST + value: postgres + - name: POSTGRES_DB + value: mydatabase + - name: POSTGRES_USER + value: myuser + - name: POSTGRES_PASSWORD + value: mypassword + - name: POSTGRES_PORT + value: "5432" + - name: DJANGO_DEBUG + value: "False" + - name: DJANGO_ALLOWED_HOSTS + value: "*" diff --git a/z2/myproject/k8s/django-deployment.yaml b/z2/myproject/k8s/django-deployment.yaml new file mode 100644 index 0000000..9bf5c5d --- /dev/null +++ b/z2/myproject/k8s/django-deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: django-app +spec: + replicas: 1 + selector: + matchLabels: + app: django + template: + metadata: + labels: + app: django + spec: + containers: + - name: django + image: django-app:latest + imagePullPolicy: Never + ports: + - containerPort: 8000 + env: + - name: POSTGRES_HOST + value: postgres + - name: POSTGRES_DB + value: mydatabase + - name: POSTGRES_USER + value: myuser + - name: POSTGRES_PASSWORD + value: mypassword + - name: POSTGRES_PORT + value: "5432" + - name: DJANGO_DEBUG + value: "False" + - name: DJANGO_ALLOWED_HOSTS + value: "*" diff --git a/z2/myproject/k8s/migrate-job.yaml b/z2/myproject/k8s/migrate-job.yaml new file mode 100644 index 0000000..fa195fc --- /dev/null +++ b/z2/myproject/k8s/migrate-job.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: django-migrate + namespace: webapp-namespace +spec: + template: + spec: + containers: + - name: migrate + image: django-app:latest + imagePullPolicy: Never + command: ["python", "manage.py", "migrate"] + env: + - name: POSTGRES_HOST + value: postgres + - name: POSTGRES_DB + value: mydatabase + - name: POSTGRES_USER + value: myuser + - name: POSTGRES_PASSWORD + value: mypassword + - name: POSTGRES_PORT + value: "5432" + restartPolicy: OnFailure + backoffLimit: 1 diff --git a/z2/myproject/k8s/namespace.yaml b/z2/myproject/k8s/namespace.yaml new file mode 100644 index 0000000..2ef198a --- /dev/null +++ b/z2/myproject/k8s/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: webapp-namespace diff --git a/z2/myproject/k8s/postgres-deployment.yaml b/z2/myproject/k8s/postgres-deployment.yaml new file mode 100644 index 0000000..bf2b95e --- /dev/null +++ b/z2/myproject/k8s/postgres-deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:13 + env: + - name: POSTGRES_DB + value: mydatabase + - name: POSTGRES_USER + value: myuser + - name: POSTGRES_PASSWORD + value: mypassword + ports: + - containerPort: 5432 + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres +spec: + selector: + app: postgres + ports: + - port: 5432 diff --git a/z2/myproject/k8s/service.yaml b/z2/myproject/k8s/service.yaml new file mode 100644 index 0000000..83fb4c8 --- /dev/null +++ b/z2/myproject/k8s/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: django-service + namespace: webapp-namespace +spec: + selector: + app: django + type: NodePort + ports: + - port: 8000 + targetPort: 8000 + nodePort: 30002 diff --git a/z2/myproject/k8s/statefulset.yaml b/z2/myproject/k8s/statefulset.yaml new file mode 100644 index 0000000..32732e3 --- /dev/null +++ b/z2/myproject/k8s/statefulset.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: postgres-pv + namespace: webapp-namespace +spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data/postgres" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: webapp-namespace +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + namespace: webapp-namespace +spec: + serviceName: "postgres" + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:13 + env: + - name: POSTGRES_DB + value: mydatabase + - name: POSTGRES_USER + value: myuser + - name: POSTGRES_PASSWORD + value: mypassword + ports: + - containerPort: 5432 + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: webapp-namespace +spec: + selector: + app: postgres + ports: + - port: 5432 diff --git a/z2/myproject/manage.py b/z2/myproject/manage.py new file mode 100755 index 0000000..92bb9a3 --- /dev/null +++ b/z2/myproject/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', 'myproject.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/z2/myproject/myapp/__init__.py b/z2/myproject/myapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/z2/myproject/myapp/admin.py b/z2/myproject/myapp/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/z2/myproject/myapp/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/z2/myproject/myapp/apps.py b/z2/myproject/myapp/apps.py new file mode 100644 index 0000000..c34fb20 --- /dev/null +++ b/z2/myproject/myapp/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MyappConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'myapp' diff --git a/z2/myproject/myapp/forms.py b/z2/myproject/myapp/forms.py new file mode 100644 index 0000000..b1c54f2 --- /dev/null +++ b/z2/myproject/myapp/forms.py @@ -0,0 +1,7 @@ +from django import forms +from .models import Student + +class StudentForm(forms.ModelForm): + class Meta: + model = Student + fields = ['name', 'age', 'university', 'degree'] diff --git a/z2/myproject/myapp/migrations/0001_initial.py b/z2/myproject/myapp/migrations/0001_initial.py new file mode 100644 index 0000000..4ab4e49 --- /dev/null +++ b/z2/myproject/myapp/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.19 on 2025-02-19 10:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('age', models.IntegerField()), + ('university', models.CharField(max_length=100)), + ('degree', models.CharField(max_length=100)), + ], + ), + ] diff --git a/z2/myproject/myapp/migrations/__init__.py b/z2/myproject/myapp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/z2/myproject/myapp/models.py b/z2/myproject/myapp/models.py new file mode 100644 index 0000000..04f732f --- /dev/null +++ b/z2/myproject/myapp/models.py @@ -0,0 +1,10 @@ +from django.db import models + +class Student(models.Model): + name = models.CharField(max_length=100) + age = models.IntegerField() + university = models.CharField(max_length=100) + degree = models.CharField(max_length=100) + + def __str__(self): + return self.name diff --git a/z2/myproject/myapp/static/styles.css b/z2/myproject/myapp/static/styles.css new file mode 100644 index 0000000..eff96a9 --- /dev/null +++ b/z2/myproject/myapp/static/styles.css @@ -0,0 +1,27 @@ +body { + font-family: Arial, sans-serif; + margin: 20px; +} + +form { + max-width: 400px; + margin: 0 auto; +} + +input, button { + width: 100%; + padding: 10px; + margin: 10px 0; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + background: #f9f9f9; + padding: 10px; + margin: 5px 0; + border: 1px solid #ddd; +} diff --git a/z2/myproject/myapp/templates/student_form.html b/z2/myproject/myapp/templates/student_form.html new file mode 100644 index 0000000..e54a223 --- /dev/null +++ b/z2/myproject/myapp/templates/student_form.html @@ -0,0 +1,17 @@ + + + + Student Form + + + +

Student Information Form

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+ View All Students + + diff --git a/z2/myproject/myapp/templates/student_list.html b/z2/myproject/myapp/templates/student_list.html new file mode 100644 index 0000000..386d60f --- /dev/null +++ b/z2/myproject/myapp/templates/student_list.html @@ -0,0 +1,16 @@ + + + + Student List + + +

Student List

+ +
+ Add New Student + + diff --git a/z2/myproject/myapp/tests.py b/z2/myproject/myapp/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/z2/myproject/myapp/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/z2/myproject/myapp/urls.py b/z2/myproject/myapp/urls.py new file mode 100644 index 0000000..d74d117 --- /dev/null +++ b/z2/myproject/myapp/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.student_form, name='student_form'), + path('list/', views.student_list, name='student_list'), +] diff --git a/z2/myproject/myapp/views.py b/z2/myproject/myapp/views.py new file mode 100644 index 0000000..abab14b --- /dev/null +++ b/z2/myproject/myapp/views.py @@ -0,0 +1,17 @@ +from django.shortcuts import render, redirect +from .forms import StudentForm +from .models import Student + +def student_form(request): + if request.method == 'POST': + form = StudentForm(request.POST) + if form.is_valid(): + form.save() + return redirect('student_list') # Redirect to a list view after submission + else: + form = StudentForm() + return render(request, 'student_form.html', {'form': form}) + +def student_list(request): + students = Student.objects.all() + return render(request, 'student_list.html', {'students': students}) diff --git a/z2/myproject/myproject/__init__.py b/z2/myproject/myproject/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/z2/myproject/myproject/__pycache__/__init__.cpython-312.pyc b/z2/myproject/myproject/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..3c761a6 Binary files /dev/null and b/z2/myproject/myproject/__pycache__/__init__.cpython-312.pyc differ diff --git a/z2/myproject/myproject/__pycache__/settings.cpython-312.pyc b/z2/myproject/myproject/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..56c589a Binary files /dev/null and b/z2/myproject/myproject/__pycache__/settings.cpython-312.pyc differ diff --git a/z2/myproject/myproject/asgi.py b/z2/myproject/myproject/asgi.py new file mode 100644 index 0000000..f36abcc --- /dev/null +++ b/z2/myproject/myproject/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for myproject 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.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') + +application = get_asgi_application() diff --git a/z2/myproject/myproject/settings.py b/z2/myproject/myproject/settings.py new file mode 100644 index 0000000..1666cde --- /dev/null +++ b/z2/myproject/myproject/settings.py @@ -0,0 +1,138 @@ +""" +Django settings for myproject project. + +Generated by 'django-admin startproject' using Django 5.1.6. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path +import os +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-y_$e4bt^whhd+8^-9nuvdmes6$g!2(d4g3keok%c&yowtq0abz' + +# 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', + 'myapp', +] + +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 = 'myproject.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'myapp/templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'myproject.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('POSTGRES_DB', 'mydatabase'), + 'USER': os.getenv('POSTGRES_USER', 'myuser'), + 'PASSWORD': os.getenv('POSTGRES_PASSWORD', 'mypassword'), + 'HOST': os.getenv('POSTGRES_HOST', 'postgres'), # Changed from 'db' to 'postgres' to match k8s service name + 'PORT': os.getenv('POSTGRES_PORT', '5432'), + } +} +# Password validation +# https://docs.djangoproject.com/en/5.1/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.1/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.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + + +# Static files (CSS, JavaScript, Images) +STATIC_URL = '/static/' +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_L10N = True +USE_TZ = True + +STATICFILES_DIRS = [os.path.join(BASE_DIR, 'myapp/static')] # Path to your static files + + diff --git a/z2/myproject/myproject/urls.py b/z2/myproject/myproject/urls.py new file mode 100644 index 0000000..338d641 --- /dev/null +++ b/z2/myproject/myproject/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for myproject project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('myapp.urls')), +] diff --git a/z2/myproject/myproject/wsgi.py b/z2/myproject/myproject/wsgi.py new file mode 100644 index 0000000..927bad5 --- /dev/null +++ b/z2/myproject/myproject/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for myproject 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.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') + +application = get_wsgi_application() diff --git a/z2/myproject/prepare-app.sh b/z2/myproject/prepare-app.sh new file mode 100755 index 0000000..511a5f2 --- /dev/null +++ b/z2/myproject/prepare-app.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Build the Django application Docker image +echo "Building Django application image..." +docker build -t django-app:latest . + +# Create a local directory for PostgreSQL data +mkdir -p /mnt/data/postgres + +echo "Application preparation complete." diff --git a/z2/myproject/remove-app.sh b/z2/myproject/remove-app.sh new file mode 100755 index 0000000..c32e620 --- /dev/null +++ b/z2/myproject/remove-app.sh @@ -0,0 +1,16 @@ +# remove-app.sh +#!/bin/bash + +set -e # Exit on any error + +echo "Removing app..." + +# Stop and remove containers, networks, volumes +docker-compose down -v + +# Remove Docker image +docker rmi myproject-web || true # Ignore errors if image doesn't exist + +# Remove named volume and network + +echo "Application removed." diff --git a/z2/myproject/requirements.txt b/z2/myproject/requirements.txt new file mode 100644 index 0000000..076c801 --- /dev/null +++ b/z2/myproject/requirements.txt @@ -0,0 +1,3 @@ +Django +gunicorn +psycopg2-binary diff --git a/z2/myproject/scripts/prepare-app.sh b/z2/myproject/scripts/prepare-app.sh new file mode 100644 index 0000000..66d2f9f --- /dev/null +++ b/z2/myproject/scripts/prepare-app.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Build Docker image +docker build -t django-app . + +# Create data directory for PV +sudo mkdir -p /mnt/data +sudo chmod 777 /mnt/data + +# Start Minikube if not running +minikube status || minikube start diff --git a/z2/myproject/scripts/start-app.sh b/z2/myproject/scripts/start-app.sh new file mode 100755 index 0000000..d3795c2 --- /dev/null +++ b/z2/myproject/scripts/start-app.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Create namespace +kubectl apply -f k8s/namespace.yaml + +# Create PV and PVC +kubectl apply -f k8s/pv-pvc.yaml + +# Create StatefulSet +kubectl apply -f k8s/statefulset.yaml + +# Create Deployment +kubectl apply -f k8s/deployment.yaml + +# Create Service +kubectl apply -f k8s/service.yaml + +# Wait for pods to be ready +kubectl wait --for=condition=Ready pod -l app=django --namespace django-namespace --timeout=120s + +# Perform migrations +kubectl exec -it $(kubectl get pods -n django-namespace -l app=django -o jsonpath='{.items[0].metadata.name}') -n django-namespace -- python manage.py migrate + +# Get service URL +minikube service django-service --url -n django-namespace diff --git a/z2/myproject/scripts/stop-app.sh b/z2/myproject/scripts/stop-app.sh new file mode 100755 index 0000000..2f75bc2 --- /dev/null +++ b/z2/myproject/scripts/stop-app.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Delete all resources in the namespace +kubectl delete namespace django-namespace + +# Delete PV (not namespaced) +kubectl delete pv postgres-pv diff --git a/z2/myproject/start-app.sh b/z2/myproject/start-app.sh new file mode 100755 index 0000000..3c89124 --- /dev/null +++ b/z2/myproject/start-app.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Apply the namespace +kubectl apply -f k8s/namespace.yaml + +# Apply the StatefulSet and related objects +kubectl apply -f k8s/statefulset.yaml + +# Apply the Django Deployment +kubectl apply -f k8s/deployment.yaml + +# Apply the Django Service +kubectl apply -f k8s/service.yaml + +echo "All Kubernetes objects created successfully." diff --git a/z2/myproject/stat.sh b/z2/myproject/stat.sh new file mode 100755 index 0000000..79f0bc5 --- /dev/null +++ b/z2/myproject/stat.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Apply the namespace +kubectl apply -f k8s/namespace.yaml + +# Apply the StatefulSet and related objects +kubectl apply -f k8s/statefulset.yaml + +# Apply the Django Deployment +kubectl apply -f k8s/deployment.yaml + +# Apply the Django Service +kubectl apply -f k8s/service.yaml + +# Wait for the PostgreSQL pod to be ready +echo "Waiting for PostgreSQL to be ready..." +kubectl wait --for=condition=Ready pod -l app=postgres -n webapp-namespace --timeout=120s + +# Apply migrations using a Kubernetes Job +echo "Applying migrations..." +kubectl apply -f k8s/migrate-job.yaml + +# Wait for the migration job to complete +kubectl wait --for=condition=Complete job/django-migrate -n webapp-namespace --timeout=120s + +echo "All Kubernetes objects created successfully." diff --git a/z2/myproject/stop-app.sh b/z2/myproject/stop-app.sh new file mode 100755 index 0000000..e29159e --- /dev/null +++ b/z2/myproject/stop-app.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Delete the Django Service +kubectl delete -f k8s/service.yaml + +# Delete the Django Deployment +kubectl delete -f k8s/deployment.yaml + +# Delete the StatefulSet and related objects +kubectl delete -f k8s/statefulset.yaml + +# Delete the namespace +kubectl delete -f k8s/namespace.yaml + +echo "All Kubernetes objects deleted successfully." diff --git a/z2/myproject/wait-for-it.sh b/z2/myproject/wait-for-it.sh new file mode 100755 index 0000000..d990e0d --- /dev/null +++ b/z2/myproject/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi