diff --git a/DockerAssignemnt.pdf b/DockerAssignemnt.pdf deleted file mode 100644 index 8415eebe..00000000 Binary files a/DockerAssignemnt.pdf and /dev/null differ diff --git a/ErfanGeramizadehNaeini/Q1/README.md b/ErfanGeramizadehNaeini/Q1/README.md new file mode 100644 index 00000000..0b3105ff --- /dev/null +++ b/ErfanGeramizadehNaeini/Q1/README.md @@ -0,0 +1,8 @@ +## postgres +``` docker run --rm -d --name pg -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=ERFAN1234 -e POSTGRES_DB=postgres -p 15432:5432 -v pgdata:/var/lib/postgresql/data postgres:14``` + +POSTGRES_PASSWORD and POSTGRES_USER for the credentials neede for authenticating +-p needed to bind the first port from the host to the docker container. +And -v needed to persist data that resides in the path specified. +at last the image is postgres:14 +I wrote a python client that I will explain in the recorded video diff --git a/ErfanGeramizadehNaeini/Q1/client.py b/ErfanGeramizadehNaeini/Q1/client.py new file mode 100644 index 00000000..6d01f003 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q1/client.py @@ -0,0 +1,75 @@ +import psycopg2 +from psycopg2 import sql + +DB_NAME = "newdb" +USER = "postgres" +PASSWORD = "ERFAN1234" +PORT = 15432 +try: + conn = psycopg2.connect( + host="localhost", + port=PORT, + database="postgres", + user=USER, + password=PASSWORD + + ) + conn.autocommit = True + cur = conn.cursor() + cur.execute("SELECT 1 FROM pg_database WHERE datname = %s;", (DB_NAME,)) + exists = cur.fetchone() + + if not exists: + cur.execute(sql.SQL("CREATE DATABASE {}").format( + sql.Identifier(DB_NAME) + )) + print(f"Database '{DB_NAME}' created.") + else: + print(f"Database '{DB_NAME}' already exists.") + + cur.close() + conn.close() + + conn = psycopg2.connect( + host="localhost", + port=PORT, + database=DB_NAME, + user=USER, + password=PASSWORD + ) + cur = conn.cursor() + + cur.execute(""" + CREATE TABLE IF NOT EXISTS test_table ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL + ); + """) + while (1): + print("1. Insert data") + print("2. Select data") + print("3. Exit") + option = int(input()) + if (option == 1): + try: + name = input("name: ") + cur.execute("INSERT INTO test_table (name) VALUES (%s);", + (name,)) + conn.commit() + except Exception as e: + print("Error:", e) + + elif (option == 2): + cur.execute("SELECT * FROM test_table;") + rows = cur.fetchall() + for row in rows: + print(row) + elif (option == 3): + print("Exiting...") + exit(0) + cur.close() + conn.close() + + +except Exception as e: + print("Error:", e) diff --git a/ErfanGeramizadehNaeini/Q1/run.sh b/ErfanGeramizadehNaeini/Q1/run.sh new file mode 100644 index 00000000..5f4526ff --- /dev/null +++ b/ErfanGeramizadehNaeini/Q1/run.sh @@ -0,0 +1 @@ +docker run --rm -d --name pg -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=ERFAN1234 -e POSTGRES_DB=postgres -p 15432:5432 -v pgdata:/var/lib/postgresql/data postgres:14 \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q2/README.md b/ErfanGeramizadehNaeini/Q2/README.md new file mode 100644 index 00000000..06dea656 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q2/README.md @@ -0,0 +1,10 @@ +## redis + +``` docker run -d --name rds -p 6390:6379 redis:7.0``` + +The explaination is the same as Q1 + +Int the first client program I set 5 keys to a value and then published hello followed by an integer to a channel then in the second program I retrieved all the keys and values and then listened to the channel for incoming messages. + +Using redis insight will be explained in the recorded video. + diff --git a/ErfanGeramizadehNaeini/Q2/first.py b/ErfanGeramizadehNaeini/Q2/first.py new file mode 100644 index 00000000..52837e1f --- /dev/null +++ b/ErfanGeramizadehNaeini/Q2/first.py @@ -0,0 +1,15 @@ +import redis +import time +CHANNEL = 'erfangchannel' +r = redis.Redis(host='localhost', port=6390, decode_responses=True) + + +for i in range(5): + r.set(f"hello{i}", f"world{i}") +i = 1 +while (1): + message = f"Hello {i}" + r.publish(CHANNEL, message) + print(f"Published: {message}") + time.sleep(1) + i += 1 diff --git a/ErfanGeramizadehNaeini/Q2/run.sh b/ErfanGeramizadehNaeini/Q2/run.sh new file mode 100644 index 00000000..c2eb3c86 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q2/run.sh @@ -0,0 +1,2 @@ +docker run -d --name rds -p 6390:6379 redis:7.0 +# redis-cli -h localhost -p 6390 ping for making sure it works \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q2/second.py b/ErfanGeramizadehNaeini/Q2/second.py new file mode 100644 index 00000000..8d06bfab --- /dev/null +++ b/ErfanGeramizadehNaeini/Q2/second.py @@ -0,0 +1,18 @@ +import redis +CHANNEL = 'erfangchannel' + +r = redis.Redis(host='localhost', port=6390, decode_responses=True) +keys = r.keys('*') +print(keys) +for key in keys: + value = r.get(key) + print(f"{key}: {value}") + +pubsub = r.pubsub() + +pubsub.subscribe(CHANNEL) +print("Subscribed to 'mychannel'...") + +for message in pubsub.listen(): + if message['type'] == 'message': + print(f"Received: {message['data']}") diff --git a/ErfanGeramizadehNaeini/Q3/README.md b/ErfanGeramizadehNaeini/Q3/README.md new file mode 100644 index 00000000..0d8a8b41 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q3/README.md @@ -0,0 +1,9 @@ +## celery +The docker compose will bring uo the redis as well as celery +I useed this bind mount so I can run docker containers outside of the existing container +- /var/run/docker.sock:/var/run/docker.sock + +Celery depends on redis. It means that runing celery container depends on running redis container. +I used CELERY_BROKER_URL and CELERY_RESULT_BACKEND for telling where to look for broker and backend(used in the tasks.py) +tasks.py defines some tasks that can be executed by calling send)task which is demonstrated in test.py + diff --git a/ErfanGeramizadehNaeini/Q3/celery/Dockerfile b/ErfanGeramizadehNaeini/Q3/celery/Dockerfile new file mode 100644 index 00000000..bcc9d086 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q3/celery/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.11-slim + +WORKDIR /app +RUN pip install celery redis docker +COPY . /app + + +CMD ["celery", "-A", "tasks", "worker", "--loglevel=info"] \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q3/celery/tasks.py b/ErfanGeramizadehNaeini/Q3/celery/tasks.py new file mode 100644 index 00000000..8c8d1d06 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q3/celery/tasks.py @@ -0,0 +1,42 @@ +from celery import Celery +import os +import docker + + +app = Celery( + 'tasks', + broker=os.environ.get('CELERY_BROKER_URL', 'redis://redis:6390/0'), + backend=os.environ.get('CELERY_RESULT_BACKEND', 'redis://redis:6390/0'), +) + +client = docker.from_env() + + +@app.task(name='tasks.add') # just for test +def add(x, y): + return x + y + + +@app.task(name='tasks.start') +def start_container(image_name, container_name=None): + try: + container = client.containers.run( + image=image_name, + name=container_name, + detach=True + ) + return f"{container.id}" + except docker.errors.APIError as e: + return f"Error starting container: {str(e)}" + + +@app.task(name='tasks.stop') +def stop_container(container_id): + try: + container = client.containers.get(container_id) + container.stop() + return f"Container {container_id} stopped" + except docker.errors.NotFound: + return f"Container {container_id} not found" + except docker.errors.APIError as e: + return f"Error stopping container: {str(e)}" diff --git a/ErfanGeramizadehNaeini/Q3/docker-compose.yaml b/ErfanGeramizadehNaeini/Q3/docker-compose.yaml new file mode 100644 index 00000000..be40f5e9 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q3/docker-compose.yaml @@ -0,0 +1,21 @@ +version: '3.8' + +services: + redis: + image: redis:7.0 + container_name: redis + expose: + - 6379 + ports: + - "6391:6379" + celery: + image: celery:5.2 + container_name: celery + build: ./celery + volumes: + - /var/run/docker.sock:/var/run/docker.sock # Mount Docker socket to allow Celery to communicate with Docker + depends_on: + - redis + environment: + - CELERY_BROKER_URL=redis://redis:6379/0 + - CELERY_RESULT_BACKEND=redis://redis:6379/0 diff --git a/ErfanGeramizadehNaeini/Q3/test.py b/ErfanGeramizadehNaeini/Q3/test.py new file mode 100644 index 00000000..9ce56ec9 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q3/test.py @@ -0,0 +1,21 @@ +from celery import Celery + +app = Celery( + 'client', + broker='redis://localhost:6391/0', + backend='redis://localhost:6391/0' +) +# initial test +print("Sending test task...") +result = app.send_task('tasks.add', args=[3, 4]) +print("Result:", result.get(timeout=10)) + +print("Starting container...") +container_id = app.send_task('tasks.start', args=[ + 'pasapples/apjctf-todo-java-app:latest']) +id = container_id.get(timeout=1000) +print("Container ID:", id) # pulling may take time + +# result = app.send_task('tasks.stop', args=[id]) +# print("Sent stop task, waiting for result...") +# print(result.get(timeout=20)) diff --git a/ErfanGeramizadehNaeini/Q45/.env.backend b/ErfanGeramizadehNaeini/Q45/.env.backend new file mode 100644 index 00000000..1892ea75 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/.env.backend @@ -0,0 +1,10 @@ +POSTGRES_USER=postgres +SECRET_KEY=wedewdedewdwefeqqfqfewfweqfeqwfewfqwfewqfeferoopwenf +POSTGRES_PASSWORD=hello +POSTGRES_DB=defaultdb +POSTGRES_PORT=5432 +DATABASE_ENGINE=postgres +CELERY_BROKER_URL=redis://redis:6379/0 +CELERY_RESULT_BACKEND=redis://redis:6379/0 +DATABASE_ENGINE=postgres +DEBUG=True diff --git a/ErfanGeramizadehNaeini/Q45/.env.celery b/ErfanGeramizadehNaeini/Q45/.env.celery new file mode 100644 index 00000000..74a567d3 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/.env.celery @@ -0,0 +1,10 @@ +CELERY_BROKER_URL=redis://redis:6379/0 +CELERY_RESULT_BACKEND=redis://redis:6379/0 +POSTGRES_DB=defaultdb +POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=hello + +POSTGRES_HOST=postgres + + diff --git a/ErfanGeramizadehNaeini/Q45/.env.database b/ErfanGeramizadehNaeini/Q45/.env.database new file mode 100644 index 00000000..c07642bd --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/.env.database @@ -0,0 +1,6 @@ +POSTGRES_PASSWORD=hello + +POSTGRES_DB=defaultdb + + +DEBUG=False \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q45/README.md b/ErfanGeramizadehNaeini/Q45/README.md new file mode 100644 index 00000000..80bc5d94 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/README.md @@ -0,0 +1,28 @@ +# web app +I wrote django application with 5 apis 3 of them is for authenticating and registering.(ignore get-ip and token/refresh ) +swagger is not implemented instead I explain my apis here + +instead of explaining APIs I implemented a swagger. + +run docker compose up --build and the visit localhost:3000/docs to see the swagger. + +## Database schema: +cloud_team is an AbstractUser means that it has everything that a normal user would have. +Like username and password and email. + +cloud_problem(name,number,description,image_name,port) +Name and number and description are obvious. +Image_name specifies the name of the image used for this problem. You can create two problems with the image name mentioned in the homework document. +Port specifies the port in the container that the image uses and should be bound to the outside port on the host. +cloud_teamproblem( team,problem,up,ip,port,container_id) +This table implements a many to many relationship betwean team and problem. +Team and problem are foreign keys. +Up is a boolean showing the status of the question container. +Ip is useless because I distinguish containers by their ports. +Port shows the port on host to access the container. +Container_id is what the name implies. It is used to stop that container if needed. + +## setting up +Setting up the application is rather easy .just look at .env files and configure them as you desire then just write docker compose up –build and hit enter. + + diff --git a/ErfanGeramizadehNaeini/Q45/celery/Dockerfile b/ErfanGeramizadehNaeini/Q45/celery/Dockerfile new file mode 100644 index 00000000..24e5a00f --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/celery/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11-slim + +WORKDIR /app +RUN pip install celery redis docker psycopg2-binary +RUN apt-get update +RUN apt-get install -y postgresql-client + + +COPY . /app + +#CMD ["celery", "-A", "tasks", "worker", "--loglevel=info"] \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q45/celery/tasks.py b/ErfanGeramizadehNaeini/Q45/celery/tasks.py new file mode 100644 index 00000000..a643b67e --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/celery/tasks.py @@ -0,0 +1,86 @@ +from psycopg2.extras import RealDictCursor +import psycopg2 +from celery import Celery +import os +import docker + + +app = Celery( + 'tasks', + broker=os.environ.get('CELERY_BROKER_URL', 'redis://redis:6390/0'), + backend=os.environ.get('CELERY_RESULT_BACKEND', 'redis://redis:6390/0'), +) + +client = docker.from_env() + + +def get_pg_connection(): + return psycopg2.connect( + host=os.environ.get('POSTGRES_HOST', 'localhost'), + port=os.environ.get('POSTGRES_PORT', '5432'), + dbname=os.environ.get('POSTGRES_DB', 'mydb'), + user=os.environ.get('POSTGRES_USER', 'myuser'), + password=os.environ.get('POSTGRES_PASSWORD', 'mypassword'), + cursor_factory=RealDictCursor + ) + + +conn = get_pg_connection() + + +@app.task(name='tasks.add') # just for test +def add(x, y): + return x + y + + +@app.task(name='tasks.start') +def start_container(teamproblemid): + try: + cur = conn.cursor() + cur.execute("""SELECT p.image_name,tp.ip,tp.port as hostport, p.port as guestport FROM cloud_teamproblem tp JOIN cloud_problem p ON tp.problem_id =p.number where tp.id=%s;""", + (teamproblemid,)) + + row = cur.fetchone() + image_name = row['image_name'] + hostport = row['hostport'] + guestport = row['guestport'] + print( + f"Starting container with image {image_name} on port {hostport}:{guestport}") + container = client.containers.run( + image=image_name, + name=None, + ports={f"{guestport}/tcp": hostport}, + detach=True + ) + + cur.execute("""UPDATE cloud_teamproblem SET container_id=%s WHERE id=%s;""", + (container.id, teamproblemid)) + conn.commit() + cur.close() + return f"{container.id}" + except docker.errors.APIError as e: + return f"Error starting container: {str(e)}" + + +@app.task(name='tasks.stop') +def stop_container(teamproblemid): + try: + cur = conn.cursor() + cur.execute("""SELECT container_id FROM cloud_teamproblem WHERE id=%s;""", + (teamproblemid,)) + container_id = cur.fetchone()['container_id'] + container = client.containers.get(container_id=container_id) + + container.stop() + + cur.execute("""UPDATE cloud_teamproblem SET container_id=NULL WHERE id=%s;""", + (teamproblemid,)) + conn.commit() + cur.close() + container.remove(force=True) + + return f"Container {container_id} stopped" + except docker.errors.NotFound: + return f"Container {container_id} not found" + except docker.errors.APIError as e: + return f"Error stopping container: {str(e)}" diff --git a/ErfanGeramizadehNaeini/Q45/cloud/Dockerfile b/ErfanGeramizadehNaeini/Q45/cloud/Dockerfile new file mode 100644 index 00000000..4626c034 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3 + +WORKDIR /app +RUN apt-get update && apt-get install -y \ + build-essential \ + libxml2-dev \ + libxslt1-dev \ + zlib1g-dev \ + python3-dev \ + libffi-dev \ + gcc +RUN pip install --upgrade pip setuptools wheel + +RUN pip install psycopg + +COPY ./requirements.txt . + +RUN pip install -r requirements.txt + +RUN pip install gunicorn + +RUN apt-get install -y postgresql-client + +COPY . . + + diff --git a/Samples and Hints/Problem 5/README.md b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__init__.py similarity index 100% rename from Samples and Hints/Problem 5/README.md rename to ErfanGeramizadehNaeini/Q45/cloud/cloud/__init__.py diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/__init__.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..7b48a0a2 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/__init__.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/admin.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..7e7545c7 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/admin.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/models.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..f6f60cd4 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/models.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/settings.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/settings.cpython-312.pyc new file mode 100644 index 00000000..5cd0c91e Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/settings.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/urls.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..6e6cf7fd Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/urls.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/views.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..2a46695f Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/__pycache__/views.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/admin.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/admin.py new file mode 100644 index 00000000..db42b2a7 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from .models import * +from django import forms + + +admin.site.register(Problem) +admin.site.register(Team) +admin.site.register(TeamProblem) diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/asgi.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/asgi.py new file mode 100644 index 00000000..9525683a --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for cloud 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/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cloud.settings') + +application = get_asgi_application() diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0001_initial.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0001_initial.py new file mode 100644 index 00000000..084f6e0a --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0001_initial.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.13 on 2025-05-01 13:08 + +from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('problem1_up', models.BooleanField(default=False)), + ('peoblem2_up', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Problem', + fields=[ + ('number', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('description', models.TextField()), + ('image_name', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='TeamProblem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('up', models.BooleanField(default=False)), + ('ip', models.CharField(blank=True, max_length=100, null=True)), + ('container_id', models.CharField(blank=True, max_length=100, null=True)), + ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cloud.problem')), + ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0002_problem_port_teamproblem_port.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0002_problem_port_teamproblem_port.py new file mode 100644 index 00000000..da0661d6 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0002_problem_port_teamproblem_port.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.13 on 2025-05-01 13:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cloud', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='problem', + name='port', + field=models.IntegerField(default=2000), + preserve_default=False, + ), + migrations.AddField( + model_name='teamproblem', + name='port', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0003_remove_team_peoblem2_up_remove_team_problem1_up.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0003_remove_team_peoblem2_up_remove_team_problem1_up.py new file mode 100644 index 00000000..920af7c9 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/0003_remove_team_peoblem2_up_remove_team_problem1_up.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.17 on 2025-05-08 19:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cloud', '0002_problem_port_teamproblem_port'), + ] + + operations = [ + migrations.RemoveField( + model_name='team', + name='peoblem2_up', + ), + migrations.RemoveField( + model_name='team', + name='problem1_up', + ), + ] diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__init__.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0001_initial.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..1691dfce Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0002_problem_port_teamproblem_port.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0002_problem_port_teamproblem_port.cpython-312.pyc new file mode 100644 index 00000000..c8493a3e Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0002_problem_port_teamproblem_port.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0003_remove_team_peoblem2_up_remove_team_problem1_up.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0003_remove_team_peoblem2_up_remove_team_problem1_up.cpython-312.pyc new file mode 100644 index 00000000..9888d657 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/0003_remove_team_peoblem2_up_remove_team_problem1_up.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/__init__.cpython-312.pyc b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..cabf1e16 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/cloud/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/models.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/models.py new file mode 100644 index 00000000..3f23dae2 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/models.py @@ -0,0 +1,28 @@ +from django.db import models +from django.contrib.auth.models import AbstractUser + + +class Team(AbstractUser): + + def __str__(self): + return self.username + + +class Problem(models.Model): + number = models.IntegerField(primary_key=True) + name = models.CharField(max_length=100) + description = models.TextField() + image_name = models.CharField(max_length=100) + port = models.IntegerField() + + def __str__(self): + return self.name + + +class TeamProblem(models.Model): + team = models.ForeignKey(Team, on_delete=models.CASCADE) + problem = models.ForeignKey(Problem, on_delete=models.CASCADE) + up = models.BooleanField(default=False) + ip = models.CharField(max_length=100, null=True, blank=True) + port = models.IntegerField(null=True, blank=True) + container_id = models.CharField(max_length=100, null=True, blank=True) diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/settings.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/settings.py new file mode 100644 index 00000000..e3269ca9 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/settings.py @@ -0,0 +1,195 @@ +""" +Django settings for cloud project. + +Generated by 'django-admin startproject' using Django 4.2.13. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" +from datetime import timedelta +import os +from pathlib import Path + +# 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/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get("SECRET_KEY", "some secret") + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = os.environ.get("DEBUG", "True") == "True" + +ALLOWED_HOSTS = ["*"] + +CORS_ALLOW_ALL_ORIGINS = True + +CORS_ORIGIN_ALLOW_ALL = True +CORS_ALLOW_ALL_ORIGINS = True # Allows any domain +CSRF_TRUSTED_ORIGINS = [ + "http://localhost:8001 ", + +] +CORS_ALLOW_CREDENTIALS = True +CORS_ALLOW_HEADERS = ["*"] +CORS_ALLOW_METHODS = ["*"] +CSRF_COOKIE_SECURE = True + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'corsheaders', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework_simplejwt.token_blacklist', + 'rest_framework_simplejwt', + 'drf_spectacular', + 'cloud', + 'drf_yasg', +] + +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', + '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 = 'cloud.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + '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 = 'cloud.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases +if os.environ.get('DATABASE_ENGINE', 'sqlite') == 'postgres': + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('POSTGRES_DB', 'postgres'), + 'USER': os.environ.get('POSTGRES_USER', 'postgres'), + 'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'password'), + 'HOST': os.environ.get('POSTGRES_HOST', 'postgres'), + 'PORT': os.environ.get('POSTGRES_PORT', '5432'), + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +# Password validation +# https://docs.djangoproject.com/en/4.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', + }, +] + +# settings.py + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ], + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ], + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + + +SPECTACULAR_SETTINGS = { + 'TITLE': 'Your API', + 'DESCRIPTION': 'API for starting/stopping instances and managing teams', + 'VERSION': '1.0.0', +} +# Internationalization +# https://docs.djangoproject.com/en/4.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/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +AUTH_USER_MODEL = 'cloud.Team' + + +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=1000), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=10), + 'ROTATE_REFRESH_TOKENS': False, + 'BLACKLIST_AFTER_ROTATION': True, + 'UPDATE_LAST_LOGIN': False, +} + +CELERY_RESULT_BACKEND = os.environ.get( + 'CELERY_RESULT_BACKEND', 'redis://localhost:6379/0') +CELERY_BROKER_URL = os.environ.get( + 'CELERY_BROKER_URL', 'redis://localhost:6379/0') + + +STATIC_URL = '/static/' +STATIC_ROOT = BASE_DIR / 'static' +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' + +# HOST_PORTION = os.environ.get('HOST_PORTION', '192.168.1') diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/urls.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/urls.py new file mode 100644 index 00000000..92e39a83 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/urls.py @@ -0,0 +1,35 @@ +""" +URL configuration for cloud project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/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 + +from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView +from .views import * +from django.conf.urls.static import static +from django.conf import settings +from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView + +urlpatterns = [ + path('admin/', admin.site.urls), + path('start-stop/', StartStopView.as_view()), + path('register/', TeamRegisterView.as_view(), name='register'), + path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), + path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + path('schema/', SpectacularAPIView.as_view(), name='schema'), + path('docs/', SpectacularSwaggerView.as_view(url_name='schema'), + name='swagger-ui'), +]+static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/views.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/views.py new file mode 100644 index 00000000..1432536d --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/views.py @@ -0,0 +1,150 @@ +from rest_framework import generics, views +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated, AllowAny +from django.contrib.auth import get_user_model +from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework import status +from celery import Celery +from .models import Problem, Team, TeamProblem +from django.conf import settings +from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiResponse, inline_serializer +from rest_framework import serializers +app = Celery( + 'client', + broker=settings.CELERY_BROKER_URL, + backend=settings.CELERY_RESULT_BACKEND +) + + +class StartStopView(views.APIView): + permission_classes = [IsAuthenticated] + + @extend_schema( + request=inline_serializer( + name='StartRequest', + fields={ + 'problem': serializers.IntegerField() + } + ), + responses={ + 200: inline_serializer(name='StartSuccess', fields={'status': serializers.CharField(), 'address': serializers.CharField()}), + 400: inline_serializer(name='StartFailure', fields={'error': serializers.CharField()}) + } + ) + def post(self, request): + problem = request.data.get("problem") + if (not problem or not Problem.objects.filter(number=problem).exists()): + return Response({"error": "problem not found"}, status=400) + instance = TeamProblem.objects.filter( + problem=problem, team=request.user).first() + if not instance: + instance = TeamProblem.objects.create( + problem=Problem.objects.get(number=problem), team=request.user) + elif instance.up: + return Response({"error": "instance already up"}, status=400) + setattr(instance, "up", True) + all_ips = TeamProblem.objects.filter( + up=True).order_by("ip").values_list('ip', flat=True) + # index = 2 + # for ip in all_ips: + # if ip == f"{settings.HOST_PORTION}.{index}": + # break + # if (index > 255): + # return Response({"error": "no ip available"}, status=400) + # setattr(instance, "ip", f"{settings.HOST_PORTION}.{index}") + + all_ports = TeamProblem.objects.filter( + up=True).order_by("port").values_list('port', flat=True) + index = 2001 + for port in all_ports: + if port != index: + break + setattr(instance, "port", f"{index}") + + instance.save() + app.send_task('tasks.start', args=[instance.id]) + + return Response({"status": "started", "address": request.get_host().split(":")[0]+":"+str(index)}, status=200) + + @extend_schema( + request=inline_serializer( + name='StopRequest', + fields={ + 'problem': serializers.IntegerField() + } + ), + responses={ + 200: inline_serializer(name='StopSuccess', fields={'status': serializers.CharField()}), + 400: inline_serializer(name='StopFailure', fields={'error': serializers.CharField()}) + } + ) + def delete(self, request): + problem = request.data.get("problem") + if (not problem or not Problem.objects.filter(number=problem).exists()): + return Response({"error": "problem not found"}, status=400) + instance = TeamProblem.objects.filter( + problem=problem, team=request.user).first() + if not instance: + return Response({"error": "instance is not up for this team"}, status=400) + elif not instance.up: + return Response({"error": "instance is not up"}, status=400) + + setattr(instance, "up", False) + instance.save() + + app.send_task('tasks.stop', args=[instance.id]) + + return Response({"status": "stoped"}, status=200) + + +# class GetIPView(views.APIView): +# permission_classes = [IsAuthenticated] + +# def get(self, request): +# problem = request.data.get("problem") +# if (not problem or not Problem.objects.filter(id=problem).exists()): +# return Response({"error": "problem not found"}, status=400) +# instance = TeamProblem.objects.filter( +# problem=problem, team=request.user).first() +# if not instance: +# return Response({"error": "instance is not up for this team"}, status=400) +# elif not instance.up: +# return Response({"error": "instance is not up"}, status=400) + +# return Response({"ip": instance.ip}, status=200) + + +class TeamRegisterView(views.APIView): + permission_classes = [AllowAny] + + @extend_schema( + request=inline_serializer( + name='RegisterRequest', + fields={ + 'username': serializers.CharField(), + 'password': serializers.CharField() + } + ), + responses={ + 201: inline_serializer(name='RegisterSuccess', fields={'access': serializers.CharField(), 'refresh': serializers.CharField()}), + 400: OpenApiResponse(description='Error') + } + ) + def post(self, request): + + team_name = request.data.get('username') + team_password = request.data.get('password') + + if not team_name or not team_password: + return Response({'error': 'username and password is required'}, status=status.HTTP_400_BAD_REQUEST) + if (Team.objects.filter(username=team_name).exists()): + return Response({'error': 'Team with this name already exists'}, status=status.HTTP_400_BAD_REQUEST) + user_model = get_user_model() + team = user_model.objects.create_user( + username=team_name, password=team_password + ) + refresh = RefreshToken.for_user(team) + return Response({ + 'access': str(refresh.access_token), + 'refresh': str(refresh) + }, status=status.HTTP_201_CREATED) diff --git a/ErfanGeramizadehNaeini/Q45/cloud/cloud/wsgi.py b/ErfanGeramizadehNaeini/Q45/cloud/cloud/wsgi.py new file mode 100644 index 00000000..15827bbf --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/cloud/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for cloud 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/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cloud.settings') + +application = get_wsgi_application() diff --git a/ErfanGeramizadehNaeini/Q45/cloud/db.sqlite3 b/ErfanGeramizadehNaeini/Q45/cloud/db.sqlite3 new file mode 100644 index 00000000..5dba7916 Binary files /dev/null and b/ErfanGeramizadehNaeini/Q45/cloud/db.sqlite3 differ diff --git a/ErfanGeramizadehNaeini/Q45/cloud/manage.py b/ErfanGeramizadehNaeini/Q45/cloud/manage.py new file mode 100644 index 00000000..d46555e7 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/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', 'cloud.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/ErfanGeramizadehNaeini/Q45/cloud/requirements.txt b/ErfanGeramizadehNaeini/Q45/cloud/requirements.txt new file mode 100644 index 00000000..d7476b7b --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/cloud/requirements.txt @@ -0,0 +1,28 @@ +asgiref==3.8.1 +certifi==2025.1.31 +charset-normalizer==3.4.1 +Django==5.1.7 +django-cors-headers==4.7.0 +django-ranged-response==0.2.0 +django-redis==5.4.0 +django-simple-captcha==0.6.2 +djangorestframework==3.15.2 +djangorestframework_simplejwt==5.5.0 +drf-yasg==1.21.10 +idna==3.10 +inflection==0.5.1 +packaging==24.2 +pillow==11.1.0 +PyJWT==2.9.0 +pytz==2025.2 +PyYAML==6.0.2 +redis==5.2.1 +requests==2.32.3 +sqlparse==0.5.3 +tzdata==2025.1 +uritemplate==4.1.1 +urllib3==2.3.0 +celery +dj-rest-auth +django-allauth +drf-spectacular \ No newline at end of file diff --git a/ErfanGeramizadehNaeini/Q45/docker-compose.yaml b/ErfanGeramizadehNaeini/Q45/docker-compose.yaml new file mode 100644 index 00000000..142d9353 --- /dev/null +++ b/ErfanGeramizadehNaeini/Q45/docker-compose.yaml @@ -0,0 +1,46 @@ +version: '3.9' + +volumes: + postgres-db: + + +services: + + postgres: + image: postgres:13 + expose: + - 5432:5432 + env_file: + - .env.database + volumes: + - postgres-db:/var/lib/postgresql/data + + backend: + build: ./cloud + env_file: + - .env.backend + ports: + - "3003:8001" + restart: always + container_name: cloudbackend + depends_on: + - postgres + command: sh -c ' until pg_isready -h postgres -p 5432; do echo "Waiting for postgres..."; sleep 1; done; python manage.py makemigrations --noinput && python manage.py migrate --noinput && python manage.py collectstatic --noinput && gunicorn cloud.wsgi:application --bind 0.0.0.0:8001' + + redis: + image: redis:7.0 + container_name: cloudredis + expose: + - 6379 + + celery: + image: celery:5.2 + container_name: cloudcelery + build: ./celery + volumes: + - /var/run/docker.sock:/var/run/docker.sock # Mount Docker socket to allow Celery to communicate with Docker + depends_on: + - redis + env_file: + - .env.celery + command: sh -c 'until pg_isready -h postgres -p 5432; do echo "Waiting for postgres..."; sleep 1; done; celery -A tasks worker --loglevel=info ' diff --git a/ErfanGeramizadehNaeini/README.md b/ErfanGeramizadehNaeini/README.md new file mode 100644 index 00000000..9574d9fe --- /dev/null +++ b/ErfanGeramizadehNaeini/README.md @@ -0,0 +1,4 @@ +### cloud assignment +each folder contains its own readme which explains the corresponding files. + +recorded video [here](here https://iutbox.iut.ac.ir/index.php/s/RdntjSHsNrweSmG) diff --git a/MohamadMahdiReisi/Problem1_PostgreSQL/README.md b/MohamadMahdiReisi/Problem1_PostgreSQL/README.md deleted file mode 100644 index 8b137891..00000000 --- a/MohamadMahdiReisi/Problem1_PostgreSQL/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/MohamadMahdiReisi/Problem2_Redis/README.md b/MohamadMahdiReisi/Problem2_Redis/README.md deleted file mode 100644 index 8b137891..00000000 --- a/MohamadMahdiReisi/Problem2_Redis/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/MohamadMahdiReisi/README.md b/MohamadMahdiReisi/README.md deleted file mode 100644 index 5fd544e5..00000000 --- a/MohamadMahdiReisi/README.md +++ /dev/null @@ -1 +0,0 @@ -Sample diff --git a/README.md b/README.md deleted file mode 100644 index e6388ed1..00000000 --- a/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Cloud Assignment - -This repository contains the **Cloud Assignment** for containerizing and managing CTF challenges using Docker, Redis, Celery, FastAPI, PostgreSQL, and NGINX. - -## 🚀 Submission Instructions - -Please follow these steps to complete and submit your assignment: - -1. **Fork this repository**: - - -2. **Create a folder with your full name** in the root of the project. - Example: -``` - MohamadMahdiReisi/ - ├── Problem1_PostgreSQL/ - ├── Problem2_Redis/ - ├── Problem3_Celery/ - ├── Problem4_WebAPI/ - ├── Problem5_NGINX/ - └── Problem6_DockerCompose/ -``` - -4. **Add all your documents, source code, and configuration files** (e.g., Dockerfiles, `docker-compose.yml`, Python scripts, etc.) to the correct folder for each question. - -5. **Upload required videos** (demonstration videos) to **IUT Box** or any cloud storage, and **add the video links** inside a `README.md` file within each problem folder. - -6. **Write your explanations and answers** as a Markdown file (`README.md`) in each problem folder. Include: -- Description of what you did -- Steps to run your solution -- Reasoning behind any decisions or tools you chose -- Screenshots if needed -- Link to your demonstration video - -7. **Open a Pull Request (PR)** back to the original repository diff --git a/Samples and Hints/Problem 1/README.md b/Samples and Hints/Problem 1/README.md deleted file mode 100644 index 386d0523..00000000 --- a/Samples and Hints/Problem 1/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Hint: SQL Commands for Question 1 - -```sql --- Create a new database -CREATE DATABASE ctf_db; - - --- Create a table to store team information -CREATE TABLE teams ( - id SERIAL PRIMARY KEY, - team_name VARCHAR(100) NOT NULL, - challenge_assigned BOOLEAN DEFAULT FALSE -); - --- Insert sample data into the table -INSERT INTO teams (team_name, challenge_assigned) -VALUES - ('Red Team', true), - ('Blue Team', false); - --- Retrieve all records from the table -SELECT * FROM teams; - --- Update a team's challenge assignment status -UPDATE teams -SET challenge_assigned = true -WHERE team_name = 'Blue Team'; - --- Delete a team from the table -DELETE FROM teams -WHERE team_name = 'Red Team'; -``` diff --git a/Samples and Hints/Problem 2/README.md b/Samples and Hints/Problem 2/README.md deleted file mode 100644 index 7c683334..00000000 --- a/Samples and Hints/Problem 2/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Hint: Redis Key-Value Example in Python - -This simple Python script shows how to connect to Redis, set a key-value pair, and retrieve it. - -```python -import redis - -# Connect to the Redis server -r = redis.Redis(host='localhost', port=6379, decode_responses=True) - -# Set a key-value pair -r.set("team:red", "assigned") - -# Get the value for the key -value = r.get("team:red") - -print(f"The value of 'team:red' is: {value}") -``` \ No newline at end of file diff --git a/Samples and Hints/Problem 3 /README.md b/Samples and Hints/Problem 3 /README.md deleted file mode 100644 index 9f0462e4..00000000 --- a/Samples and Hints/Problem 3 /README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Hint: Basic Celery Task (Without Redis) - -This example shows how to define and run a basic Celery task that prints `'doing task'`. - -### 📄 `tasks.py` - -```python -from celery import Celery - -app = Celery('simple_task', broker='memory://') - -@app.task -def do_something(): - print("doing task") -``` - -### 📄 `main.py` - -```python -from tasks import do_something - -do_something.delay() -``` - diff --git a/Samples and Hints/Problem 4/README.md b/Samples and Hints/Problem 4/README.md deleted file mode 100644 index 627c908f..00000000 --- a/Samples and Hints/Problem 4/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Hint: Simple Flask Echo API for Question 4 - -This example Flask app echoes back any JSON data sent to it via POST requests. - -### 📄 `app.py` - -```python -from flask import Flask, request, jsonify - -app = Flask(__name__) - -@app.route('/echo', methods=['POST']) -def echo(): - data = request.get_json() - return jsonify({ - "you_sent": data - }) - -if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5000) -``` \ No newline at end of file