diff --git a/morozov_vladimir_lab_3/.gitignore b/morozov_vladimir_lab_3/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/morozov_vladimir_lab_3/docker-compose.yml b/morozov_vladimir_lab_3/docker-compose.yml new file mode 100644 index 0000000..1abc057 --- /dev/null +++ b/morozov_vladimir_lab_3/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' +# Объявляем сервисы +services: + artists_srv: # сервис приложения артистов + build: ./service_artists/ + + paints_srv: # сервис приложения картин + build: ./service_paints/ + + gateway: # сервис nginx + image: nginx:latest # назначаем его образ + ports: # пробрасываем порта + - 8080:8080 + volumes: # перекидываем конфигурационный файл + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: # ожидаем запуска предыдущих сервисов + - artists_srv + - paints_srv diff --git a/morozov_vladimir_lab_3/nginx.conf b/morozov_vladimir_lab_3/nginx.conf new file mode 100644 index 0000000..f9f9e23 --- /dev/null +++ b/morozov_vladimir_lab_3/nginx.conf @@ -0,0 +1,39 @@ +events { + worker_connections 1024; +} +http +{ + + server { + listen 8080; + listen [::]:8080; + server_name localhost; + + location /artists/ { + proxy_pass http://artists_srv:8001/artists/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /artists/docs { + proxy_pass http://artists_srv:8001/docs; + proxy_set_header Host $host; + } + + location /paints/docs { + proxy_pass http://paints_srv:8002/docs; + proxy_set_header Host $host; + } + + location /paints/ { + proxy_pass http://paints_srv:8002/paints/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + } +} \ No newline at end of file diff --git a/morozov_vladimir_lab_3/readme.md b/morozov_vladimir_lab_3/readme.md new file mode 100644 index 0000000..257aab1 --- /dev/null +++ b/morozov_vladimir_lab_3/readme.md @@ -0,0 +1,26 @@ +# Лабораторная работа №3 - REST API, Gateway и синхронный обмен между микросервисами + +## Разработанные приложения +В рамках л/р были разработаны 2 приложения: +1) Сервис для работы с сущностью художник(artist). Сущность имеет следующие поля: + 1) uid (номер) + 2) name (ФИО) + 3) year (год рождения) +2) Сервис для работы с сущностью картина(paint). Сущность имеет следующие поля: + 1) uid (номер) + 2) title (название) + 3) year (год создания) + 4) author (номер художника) +Один художник(один) может создать несколько картин(многие) +## Приготовления +Для обеспечения нужной работоспособности нашего приложения был использован веб-сервер Nginx, который можем использовать в роли прокси сервера. +Для этого нужно было развернуть его в отдельном контейнере и настроить его конфигурационный файл, в которым мы описывали прослушиваемые адреса и куда нужно перенаправлять. +Сервисы развернули в обычном порядке: написали код приложения, создали докер-файл, записали в докер-композе. +## Запуск +Для запуска приложения используем команду: +``` +docker-compose up --build +``` + +## Запись тестирования +Работа приложения представлена в [видео](https://disk.yandex.ru/i/8yuoSm0QSR9wGw) \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_artists/app.py b/morozov_vladimir_lab_3/service_artists/app.py new file mode 100644 index 0000000..94deccc --- /dev/null +++ b/morozov_vladimir_lab_3/service_artists/app.py @@ -0,0 +1,77 @@ +import sys +import uuid +import uvicorn + +from fastapi import FastAPI, Body +from fastapi.responses import JSONResponse +from fastapi.encoders import jsonable_encoder + +app = FastAPI(title="Artists service") +# Сервис для работы с художниками +# класс сущности художник +class artist: + def __init__(self, name, year, uid=""): + if len(uid) == 0: + self.uid = str(uuid.uuid4()) + else: + self.uid = uid + self.name = name + self.year = year + +# Первоначальные данные +artists = [ + artist("Vincent van Gogh","1853","d1c02854-76d8-4a35-be5c-076f091a67c0"), + artist("Claude Monet","1840","41e2dadc-1aa8-4f14-b1e8-7fc5664d1cdf"), + artist("Ilya Repin","1844", "591f8c2f-1937-44a1-b1f8-ff1f8b52cece")] +# Получение всех художников +@app.get("/artists/") +async def get_all(): + json_data = jsonable_encoder(artists) + return JSONResponse(json_data) +# Получение конкретного художника +@app.get("/artists/{uid}") +def get_uid(uid): + for art in artists: + if art.uid == uid: + json_data = jsonable_encoder(art) + return JSONResponse(json_data) + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) +# Создание нового художника +@app.post("/artists/") +def create(name = Body(embed=True), year = Body(embed=True)): + for art in artists: + if art.name.lower() == name.lower(): + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + new_artist = artist(name,year) + artists.append(new_artist) + json_data = jsonable_encoder(new_artist) + return JSONResponse(json_data) +# Изменение данных о художники +@app.put("/artists/{uid}") +def update(uid, name = Body(embed=True), year = Body(embed=True)): + for art in artists: + if uid == art.uid: + art.name = name + art.year = year + json_data = jsonable_encoder(art) + return JSONResponse(json_data) + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) +# Удаление художника +@app.delete("/artists/{uid}") +def delete(uid): + ind = 0 + find = False + for art in artists: + if art.uid == uid: + find = True + break + ind += 1 + + if find: + artists.pop(ind) + return JSONResponse(content={"message": "Resource Deleted"}, status_code=200) + else: + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + +if __name__ == '__main__' : + uvicorn.run("app:app",host="0.0.0.0", port=8001,) \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_artists/dockerfile b/morozov_vladimir_lab_3/service_artists/dockerfile new file mode 100644 index 0000000..3d28ad3 --- /dev/null +++ b/morozov_vladimir_lab_3/service_artists/dockerfile @@ -0,0 +1,10 @@ +# объявляем базовый образ, на основе которого будет все построено +FROM "python:3.9-slim" +# назначаем основную рабочую директорию +WORKDIR /usr/src/app +# копируем содержимое текущей папке (скрипт питона) в контейнер, в основную рабочую папку +COPY . . +# установка req +RUN pip install -r req.txt +# при запуске образа выполняем команду запуска приложения +CMD [ "python", "app.py"] \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_artists/req.txt b/morozov_vladimir_lab_3/service_artists/req.txt new file mode 100644 index 0000000..d89375d --- /dev/null +++ b/morozov_vladimir_lab_3/service_artists/req.txt @@ -0,0 +1,4 @@ +fastapi[all] +uuid +uvicorn +requests \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_paints/app.py b/morozov_vladimir_lab_3/service_paints/app.py new file mode 100644 index 0000000..d05fa9e --- /dev/null +++ b/morozov_vladimir_lab_3/service_paints/app.py @@ -0,0 +1,95 @@ +import sys +import uuid +import requests +import uvicorn + +from fastapi import FastAPI, Body +from fastapi.responses import JSONResponse +from fastapi.encoders import jsonable_encoder + +app = FastAPI(title="Paints service") +# Сервис для работы с картинами +# Класс картины +class paint: + def __init__(self, title,year,author): + self.uid = str(uuid.uuid4()) + self.title = title + self.year = year + self.author = author + +# Первоначальные данные +paints = [ + paint("Sunflowers","1887", "d1c02854-76d8-4a35-be5c-076f091a67c0"), + paint("Self-portrait with a severed ear and a tube","1889", "d1c02854-76d8-4a35-be5c-076f091a67c0"), + paint("Boatmen on the Volga","1873", "591f8c2f-1937-44a1-b1f8-ff1f8b52cece")] + +# Получение всех картин +@app.get("/paints/") +async def get_all(): + json_data = jsonable_encoder(paints) + return JSONResponse(json_data) +# Получение конкретной картины +@app.get("/paints/{uid}") +def get_uid(uid): + for art in paints: + if art.uid == uid: + response = requests.get(f"http://artists_srv:8001/artists/{art.author}") + author = response.json() + result = { + "uid":art.uid, + "title":art.title, + "year":art.year, + "authorUid":art.author, + "author":author + } + json_data = jsonable_encoder(result) + return JSONResponse(json_data) + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) +# Создание новой картины +@app.post("/paints/") +def create(title = Body(embed=True), year = Body(embed=True), author = Body(embed=True)): + for art in paints: + if art.title.lower() == title.lower(): + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + response = requests.get(f"http://artists_srv:8001/artists/{author}") + if response.status_code == 404: + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + new_paint = paint(title,year, author) + paints.append(new_paint) + json_data = jsonable_encoder(new_paint) + return JSONResponse(json_data) +# Обновление данных о картине +@app.put("/paints/{uid}") +def update(uid, title = Body(embed=True), year = Body(embed=True), author = Body(embed=True)): + response = requests.get(f"http://artists_srv:8001/artists/{author}") + print(author) + print(response.status_code) + if response.status_code == 404: + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + for art in paints: + if uid == art.uid: + art.title = title + art.year = year + art.author = author + json_data = jsonable_encoder(art) + return JSONResponse(json_data) + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) +# Удаление картины +@app.delete("/paints/{uid}") +def delete(uid): + ind = 0 + find = False + for art in paints: + if art.uid == uid: + find = True + break + ind += 1 + + if find: + paints.pop(ind) + return JSONResponse(content={"message": "Resource Deleted"}, status_code=200) + else: + return JSONResponse(content={"message": "Resource Not Found"}, status_code=404) + +if __name__ == '__main__' : + uvicorn.run("app:app", host="0.0.0.0", port=8002,) \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_paints/dockerfile b/morozov_vladimir_lab_3/service_paints/dockerfile new file mode 100644 index 0000000..4ee7001 --- /dev/null +++ b/morozov_vladimir_lab_3/service_paints/dockerfile @@ -0,0 +1,10 @@ +# объявляем базовый образ, на основе которого будет все построено +FROM "python:3.9-slim" +# назначаем основную рабочую директорию +WORKDIR /usr/src/app +# копируем содержимое текущей папке (скрипт питона) в контейнер, в основную рабочую папку +COPY . . +# установка зависимостей +RUN pip install -r req.txt +# при запуске образа выполняем команду запуска приложения +CMD [ "python", "app.py"] \ No newline at end of file diff --git a/morozov_vladimir_lab_3/service_paints/req.txt b/morozov_vladimir_lab_3/service_paints/req.txt new file mode 100644 index 0000000..d89375d --- /dev/null +++ b/morozov_vladimir_lab_3/service_paints/req.txt @@ -0,0 +1,4 @@ +fastapi[all] +uuid +uvicorn +requests \ No newline at end of file