From 2af22040cc9079a2a1a69784593855c65929437e Mon Sep 17 00:00:00 2001 From: Filipe Suhett Date: Wed, 26 Feb 2025 13:37:19 -0300 Subject: [PATCH 1/4] add-readme --- README.md | 378 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 300 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 05fdafe..be39a23 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,326 @@ -# Desafio DevOps - LEDS -*Bem-vindo!* 👋 +# LedsChallenge - Documentação Completa -Neste desafio, você terá a oportunidade de demonstrar que possui as habilidades necessárias para atuar no time de DevOps do laboratório. +Este repositório é uma solução DevOps completa, abrangendo a configuração do **Dockerfile**, **Terraform**, **GitHub Actions** e a **Infraestrutura na AWS** para uma aplicação FastAPI. A seguir, você encontrará todos os detalhes das configurações, decisões tomadas e passos necessários para replicar e entender o processo. -# Contextualização +## Índice -O desafio é implementar uma automação de CI/CD usando Github Actions para um programa que permita realizar as seguintes buscas: -1. Listar os **órgãos, códigos e editais dos concursos públicos** que se encaixam no perfil do candidato, tomando como base o seu **CPF**; -2. Listar o **nome, data de nascimento e o CPF** dos candidatos que se encaixam no perfil do concurso tomando com base o **Código do Concurso** do concurso público; +1. [Pré-requisitos](#pré-requisitos) +2. [Estrutura do Repositório](#estrutura-do-repositório) +3. [Dockerfile](#dockerfile) +4. [GitHub Actions](#github-actions) +5. [Terraform](#terraform) + - [Provedor AWS](#provedor-aws) + - [Repositório ECR](#repositório-ecr) + - [Instância EC2](#instância-ec2) + - [IAM e Políticas](#iam-e-políticas) +6. [Como funciona a API](#como-funciona-a-api) +7. [Infraestrutura AWS](#infraestrutura-aws) +8. [Passos para Rodar Localmente](#passos-para-rodar-localmente) +9. [Notas Finais](#notas-finais) -A automação deve realizar as seguintes automações: -1. Executar os testes automatizados -2. Verificar a qualidade do código -3. Quebrar o CI quando os testes falharem ou quando a qualidade for menor de 80% -4. Registrar o docker do software no Github Package +--- -O arquivo **candidatos.txt** contém as informações dos candidatos: +## 1. Pré-requisitos -| Nome | Data de Nascimento | CPF | Profissões| -|---|---|---|---| -| Lindsey Craft | 19/05/1976 | 182.845.084-34 | [carpinteiro] | -| Jackie Dawson | 14/08/1970 | 311.667.973-47 | [marceneiro, assistente administrativo] | -| Cory Mendoza | 11/02/1957 | 565.512.353-92 | [carpinteiro, marceneiro] | +Antes de começar, verifique se você tem as ferramentas e configurações necessárias em seu ambiente: -O arquivo **concursos.txt** contém as informações dos concursos públicos: +- **Docker**: Para construir e rodar containers. +- **Terraform**: Para provisionamento de infraestrutura na AWS. +- **AWS CLI**: Para gerenciar e configurar recursos AWS diretamente do seu terminal. +- **GitHub Actions**: Para automação do CI/CD. +- **Conta AWS**: Com permissões apropriadas para criar e gerenciar recursos como EC2, IAM, ECR, etc. -| Órgão | Edital | Código do Concurso | Lista de vagas| -|---|---|---|---| -| SEDU | 9/2016 | 61828450843 | [analista de sistemas, marceneiro] | -| SEJUS | 15/2017 | 61828450843 | [carpinteiro,professor de matemática,assistente administrativo] | -| SEJUS | 17/2017 | 95655123539 | [professor de matemática] | +--- -🤩 **As tecnologias a serem utilizadas na implementação da solução ficam a seu critério!** +## 2. Estrutura do Repositório -# Como entregar? -1. Faça um **fork** do repositório. Nesse fork esperamos encontrar uma documentação completa da solução e a listagem dos diferenciais implementados. -2. Abra um **pull request (PR)** do seu fork para o nome repositório com o seu nome como título. Assim conseguimos te localizar melhor e ver que você já finalizou o desafio! +O repositório segue uma estrutura simples, porém organizada, para facilitar a navegação e execução de todos os componentes necessários: -🚨 **Atenção**: você deve enviar apenas o código fonte. Não serão aceitos códigos compilados. +``` +. +├── Dockerfile # Configuração da imagem Docker +├── requirements.txt # Dependências Python +├── api/ # Código da aplicação FastAPI +│ ├── main.py # Arquivo principal da aplicação +│ ├── schemas.py # Definições dos schemas Pydantic +│ ├── models.py # Modelos de dados +│ └── database.py # Conexão com o banco de dados +├── tests/ # Testes da aplicação +│ ├── conftest.py # Configurações de testes +│ ├── test_candidatos.py # Testes dos candidatos +│ └── test_concursos.py # Testes dos concursos +├── terraform/ # Infraestrutura com Terraform +│ ├── main.tf # Definições principais do Terraform +│ ├── variables.tf # Variáveis do Terraform +│ └── outputs.tf # Saídas do Terraform +├── .github/ # Configurações do GitHub Actions +│ └── workflows/ +│ └── deploy.yml # Workflow de deploy +└── README.md # Documentação do projeto +``` -## Avaliação +--- -O programa será avaliado levando em conta os seguintes critérios: +## 3. Dockerfile -| Critério | Valor | -|---|---| -| Legibilidade do Código | 10 | -| Documentação do código | 10 | -| Documentação da solução | 10 | -| Tratamento de Erros | 10 | -| Implementar testes unitários | 15 | -| Implementar integração com [Github Action](https://github.com/features/actions) | 10 | -| Implementar integração com Github Action + SonarQube | 10 | -| Implementar usando Docker | 5 | -| Total | 90 | +O `Dockerfile` define a imagem Docker necessária para rodar a aplicação FastAPI dentro de um container. -A sua pontuação será a soma dos valores obtidos nos critérios acima. +```dockerfile +# Usando a imagem base do Python 3.11 com uma versão slim para reduzir o tamanho da imagem +FROM python:3.11-slim -## Diferenciais -Você pode **aumentar sua pontuação** implementando os seguintes diferenciais: +# Definindo uma variável de ambiente para a URL do banco de dados +ARG DATABASE_URL +ENV DATABASE_URL=$DATABASE_URL -| Item | Pontos Ganhos | -|---|---| -| Criar um [serviço](https://martinfowler.com/articles/microservices.html) com o problema | 30 | -| Utilizar banco de dados | 30 | -| Implementar Clean Code | 20 | -| Implementar o padrão de programação da tecnologia escolhida | 20 | -| Implementar testes comportamentais | 15 | -| Usar tecnologias de IaC (Terraform, ansible, HelmChart, etc)|15| -| Total| 130 | +# Definindo o diretório de trabalho dentro do container +WORKDIR /app -A pontuação final será calculada somando os critérios obrigatórios e os diferenciais implementados corretamente. +# Copiando o arquivo de dependências para dentro do container +COPY requirements.txt . -# Penalizações +# Instalando as dependências do requirements.txt +RUN pip install --no-cache-dir -r requirements.txt -Você será desclassificado se: +# Copiando todos os arquivos do diretório atual para dentro do container +COPY . . -1. Enviar uma solução que não funcione. -2. Não cumprir os critérios da seção **Avaliação**. -3. For identificado plágio. - -***Que a força esteja com você. Boa sorte!*** +# Expondo a porta 8000 para a aplicação FastAPI +EXPOSE 8000 -
-
+# Comando para iniciar a aplicação FastAPI +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] +``` -### +### Explicação: +- **FROM python:3.11-slim**: Usa a versão leve do Python 3.11. +- **COPY requirements.txt**: Copia o arquivo de dependências para o container. +- **RUN pip install**: Instala as dependências Python no container. +- **CMD**: Inicia o servidor `uvicorn` para rodar a aplicação FastAPI na porta 8000. -
+--- -
- - linkedin logo - - - instagram logo - - - youtube logo - -
+## 4. GitHub Actions -### \ No newline at end of file +A configuração de CI/CD está automatizada no GitHub Actions. O workflow, localizado em `.github/workflows/deploy.yml`, define todos os passos para automação de build, testes e deploy. + +### Workflow de Exemplo + +```yaml +name: CI/CD Pipeline + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run tests with coverage + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + run: pytest --cov=api --cov-report=xml + + sonar: + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Download coverage report + uses: actions/download-artifact@v4 + with: + name: coverage-report + - name: Install SonarScanner + run: | + curl -sSLo sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.0.2.4839-linux-x64.zip + unzip sonar-scanner.zip -d $HOME + echo "$HOME/sonar-scanner-7.0.2.4839-linux-x64/bin" >> $GITHUB_PATH + - name: Run SonarQube scan + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: sonar-scanner \ + -Dsonar.organization=filipesuhett \ + -Dsonar.projectKey=filipesuhett_ledschallenge \ + -Dsonar.sources=api \ + -Dsonar.python.coverage.reportPaths=coverage.xml \ + -Dsonar.host.url=https://sonarcloud.io +``` + +Este workflow faz o seguinte: +1. **Testes**: Executa os testes e gera um relatório de cobertura. +2. **SonarQube**: Faz a análise de qualidade de código com SonarQube. +3. **Infraestrutura**: Provisiona a infraestrutura utilizando Terraform. +4. **Build Docker**: Constrói a imagem Docker. +5. **Deploy**: Realiza o deploy da aplicação na AWS. + +--- + +## 5. Terraform + +O **Terraform** é utilizado para provisionar a infraestrutura na AWS. A configuração está dividida nos seguintes arquivos: + +### 5.1 Provedor AWS + +Configuração para autenticar e interagir com a AWS: + +```hcl +provider "aws" { + region = var.aws_region +} +``` + +### 5.2 Repositório ECR + +Configuração para criar um repositório ECR para armazenar as imagens Docker: + +```hcl +resource "aws_ecr_repository" "app_repo" { + name = "ledschallenge-repo" +} +``` + +### 5.3 Instância EC2 + +Provisiona uma instância EC2 para rodar a aplicação: + +```hcl +resource "aws_instance" "app_instance" { + ami = var.ami_id + instance_type = "t2.micro" +} +``` + +### 5.4 IAM e Políticas + +Define as permissões necessárias para interagir com outros recursos da AWS: + +```hcl +resource "aws_iam_role" "app_role" { + name = "ledschallenge-role" + assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json +} +``` + +--- + +## 6. Como funciona a API + +A aplicação foi desenvolvida utilizando **FastAPI** e se comunica com um banco de dados relacional via **SQLAlchemy**. A API possui dois endpoints principais para buscar concursos e candidatos com base em um CPF ou código do concurso. + +### 6.1 Dependências da API + +A API depende das seguintes bibliotecas principais: +- `fastapi`: Para criação dos endpoints. +- `sqlalchemy`: Para interagir com o banco de dados. +- `pydantic`: Para definição dos schemas de entrada e saída. + +A conexão com o banco de dados é gerenciada pelo **SQLAlchemy** e a sessão do banco é obtida através da função `get_db()`. + +### 6.2 Endpoints Disponíveis + +#### **Listar Concursos por CPF** +**Rota:** `GET /concursos/{cpf}` + +**Descrição:** Retorna a lista de concursos disponíveis para um candidato com base em seu CPF. O sistema verifica quais profissões o candidato possui e filtra os concursos compatíveis com essas profissões. + +**Exemplo de Requisição:** +```http +GET /concursos/12345678900 +``` + +**Exemplo de Resposta:** +```json +[ + { + "orgao": "SEDU", + "edital": "4/2017", + "codigo": "8972983713", + "vagas": ["Engenheiro", "Analista de TI"] + } +] +``` + +**Possíveis Erros:** +- `404 Not Found`: Caso o CPF não seja encontrado no banco de dados. + +--- + +#### **Listar Candidatos por Código de Concurso** +**Rota:** `GET /candidatos/{codigo_concurso}` + +**Descrição:** Retorna a lista de candidatos compatíveis com um determinado concurso, com base nas profissões exigidas para as vagas disponíveis. + +**Exemplo de Requisição:** +```http +GET /candidatos/CONCURSO123 +``` + +**Exemplo de Resposta:** +```json +[ + { + "nome": "João Silva", + "cpf": "12345678900", + "data_nascimento": "1976-05-19", + "profissoes": ["Engenheiro"] + } +] +``` + +**Possíveis Erros:** +- `404 Not Found`: Caso o código do concurso não seja encontrado no banco de dados. + +--- + +### 6.3 Banco de Dados + +A API utiliza um banco de dados relacional, onde há duas tabelas principais: +- **Candidatos** (`Candidato`): Contém informações dos candidatos, incluindo CPF, nome e profissões. +- **Concursos** (`Concurso`): Contém informações sobre os concursos, incluindo código, nome e as profissões exigidas. + +Os relacionamentos entre essas tabelas permitem que a API filtre os concursos e candidatos de forma eficiente. + +--- + +## 7. Infraestrutura AWS + +Aqui você vai encontrar todas as configurações necessárias para rodar a aplicação na AWS, incluindo a criação de EC2, IAM, ECR, e mais. As configurações detalhadas estão localizadas na pasta `terraform`. + +--- + +## 8. Passos para Rodar Localmente + +Se você deseja rodar o projeto localmente, siga os passos abaixo: + +1. **Instale as dependências**: + - Certifique-se de que você tenha o `Docker` e o `Terraform` instalados. + - Execute `pip install -r requirements.txt` para instalar as dependências Python. + +2. **Rodando a aplicação com Docker**: + - Construa a imagem Docker com o comando `docker build -t ledschallenge .` + - Execute a aplicação com `docker run -p 8000:8000 ledschallenge`. + +3. **Testando localmente**: + - Use `pytest` para rodar os testes locais: `pytest`. + +--- + +## 9. Notas Finais + +Este repositório tem como objetivo proporcionar uma solução completa e automatizada para o deployment de uma aplicação FastAPI usando práticas de DevOps. Ele integra Terraform para provisionamento de infraestrutura, GitHub Actions para automação de CI/CD e Docker para a criação de ambientes isolados. + +--- \ No newline at end of file From ad94cf74fb2795cbc3e7c704ba7f9025fc31a3cb Mon Sep 17 00:00:00 2001 From: Filipe Suhett Date: Wed, 26 Feb 2025 13:38:48 -0300 Subject: [PATCH 2/4] asas --- .dockerignore | 4 + .github/workflows/docker-build.yml | 191 +++++++++++++++++++++++++++++ Dockerfile | 24 ++++ api/database.py | 19 +++ api/main.py | 55 +++++++++ api/models.py | 21 ++++ api/schemas.py | 24 ++++ requirements.txt | 8 ++ terraform/main.tf | 97 +++++++++++++++ terraform/variables.tf | 18 +++ tests/conftest.py | 25 ++++ tests/test_candidatos.py | 28 +++++ tests/test_concursos.py | 26 ++++ 13 files changed, 540 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-build.yml create mode 100644 Dockerfile create mode 100644 api/database.py create mode 100644 api/main.py create mode 100644 api/models.py create mode 100644 api/schemas.py create mode 100644 requirements.txt create mode 100644 terraform/main.tf create mode 100644 terraform/variables.tf create mode 100644 tests/conftest.py create mode 100644 tests/test_candidatos.py create mode 100644 tests/test_concursos.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6d75dcc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ + +tests/ +.github/ +terraform/ \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..4bd88ca --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,191 @@ +# Definição do nome da pipeline CI/CD +name: CI/CD Pipeline + +# Configura os eventos que disparam o pipeline, sendo um push ou pull request na branch "main" +on: + push: + branches: ["main"] # Dispara no push para a branch main + paths-ignore: + - README.md # Ignora alterações no arquivo README.md + pull_request: + branches: ["main"] # Dispara quando um pull request é aberto para a branch main + paths-ignore: + - README.md # Ignora alterações no arquivo README.md + +jobs: + # Job de testes + test: + runs-on: ubuntu-latest # Roda em uma máquina virtual com Ubuntu mais recente + steps: + # Passo 1: Faz o checkout do repositório + - name: Checkout repository + uses: actions/checkout@v3 # Usa a ação de checkout do repositório + + # Passo 2: Configura o Python 3.11 no ambiente + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' # Define a versão do Python a ser utilizada + + # Passo 3: Instala as dependências do projeto usando o pip + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt # Instala as dependências do arquivo requirements.txt + + # Passo 4: Define o PYTHONPATH para o diretório atual + - name: Set PYTHONPATH + run: echo "PYTHONPATH=$(pwd)" >> $GITHUB_ENV # Define a variável de ambiente PYTHONPATH + + # Passo 5: Executa os testes com cobertura de código + - name: Run tests with coverage + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} # Passa a variável DATABASE_URL como segredo + run: | + pytest --cov=api --cov-report=xml # Executa os testes com cobertura e gera relatório XML + + # Passo 6: Faz o upload do relatório de cobertura + - name: Upload coverage report + uses: actions/upload-artifact@v4 # Usa a ação para enviar o relatório de cobertura como artefato + with: + name: coverage-report + path: coverage.xml # O caminho do arquivo de cobertura + + # Job de análise do SonarQube + sonar: + runs-on: ubuntu-latest # Roda em uma máquina Ubuntu + needs: test # Esse job depende do job "test" para ser executado + steps: + # Passo 1: Faz o checkout do repositório + - name: Checkout repository + uses: actions/checkout@v3 + + # Passo 2: Baixa o relatório de cobertura + - name: Download coverage report + uses: actions/download-artifact@v4 + with: + name: coverage-report # Baixa o artefato de cobertura gerado no job anterior + + # Passo 3: Baixa e instala o SonarScanner + - name: Download and Install SonarScanner + run: | + curl -sSLo sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.0.2.4839-linux-x64.zip + unzip sonar-scanner.zip -d $HOME # Baixa e descompacta o scanner + echo "$HOME/sonar-scanner-7.0.2.4839-linux-x64/bin" >> $GITHUB_PATH # Adiciona o sonar-scanner ao PATH + + # Passo 4: Executa a análise no SonarQube + - name: SonarQube Scan + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Passa o token de autenticação como segredo + run: | + sonar-scanner \ + -Dsonar.organization=filipesuhett \ + -Dsonar.projectKey=filipesuhett_ledschallenge \ + -Dsonar.sources=api \ + -Dsonar.python.coverage.reportPaths=coverage.xml \ + -Dsonar.host.url=https://sonarcloud.io # Configura o SonarQube e envia os dados de cobertura + + # Job de infraestrutura usando Terraform + terraform: + runs-on: ubuntu-latest # Roda em uma máquina Ubuntu + needs: sonar # Esse job depende do job "sonar" + steps: + # Passo 1: Faz o checkout do repositório + - name: Checkout repository + uses: actions/checkout@v3 + + # Passo 2: Configura as credenciais da AWS + - name: Configure AWS CLI + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # Usando a chave de acesso da AWS como segredo + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Usando a chave secreta da AWS como segredo + aws-region: ${{ secrets.AWS_REGION }} # Região da AWS + + # Passo 3: Instala o Terraform + - name: Instalar Terraform + uses: hashicorp/setup-terraform@v2 # Instala o Terraform no ambiente + + # Passo 4: Navega até o diretório Terraform e executa comandos + - name: Navegar até a pasta terraform e inicializar + run: | + cd terraform + terraform init # Inicializa o diretório com o Terraform + terraform plan -out=tfplan # Planeja a execução + terraform show -no-color tfplan # Exibe o plano sem cores + + # Passo 5: Aplica o plano do Terraform + - name: Aplicar Terraform + run: | + cd terraform + terraform apply -auto-approve # Aplica as mudanças sem confirmação manual + + # Job de construção do Docker + build: + runs-on: ubuntu-latest # Roda em uma máquina Ubuntu + needs: terraform # Esse job depende do job "terraform" + steps: + # Passo 1: Faz o checkout do repositório + - name: Checkout repository + uses: actions/checkout@v3 + + # Passo 2: Constrói a imagem Docker com o segredo DATABASE_URL + - name: Build Docker image with secret DATABASE_URL + run: | + docker build --build-arg DATABASE_URL=${{ secrets.DATABASE_URL }} -t ledschallenge:latest . # Constrói a imagem Docker com o argumento de banco de dados + + # Passo 3: Salva a imagem Docker em um arquivo tar + - name: Save Docker image as tar file + run: | + docker save -o ledschallenge.tar ledschallenge:latest # Salva a imagem em formato .tar + + # Passo 4: Faz o upload do arquivo tar como artefato + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ledschallenge-image + path: ledschallenge.tar # Envia a imagem como artefato + + # Job de deployment + deploy: + runs-on: ubuntu-latest # Roda em uma máquina Ubuntu + needs: build # Esse job depende do job "build" + steps: + # Passo 1: Faz o checkout do repositório + - name: Checkout repository + uses: actions/checkout@v3 + + # Passo 2: Configura as credenciais da AWS para o deployment (caso precise de algum recurso da AWS) + - name: Configure AWS CLI + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # Chave de acesso da AWS + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Chave secreta da AWS + aws-region: ${{ secrets.AWS_REGION }} # Região da AWS + + # Passo 3: Faz login no Docker Hub usando o token pessoal + - name: Docker Hub Login + run: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_TOKEN }} # Login com o token + + # Passo 4: Baixa a imagem Docker criada no job anterior + - name: Baixar imagem do build + uses: actions/download-artifact@v4 + with: + name: ledschallenge-image + path: ./ # Baixa o artefato contendo a imagem Docker + + # Passo 5: Carrega a imagem Docker no ambiente + - name: Load Docker image + run: docker load -i ledschallenge.tar # Carrega a imagem Docker + + # Passo 6: Marca e faz push da imagem para o Docker Hub + - name: Tag e Push para Docker Hub + run: | + IMAGE_TAG=latest + REPO_URI=docker.io/${{ secrets.DOCKER_USERNAME }}/ledschallenge # Substitua pelo seu repositório no Docker Hub + + docker tag ledschallenge:latest $REPO_URI:$IMAGE_TAG # Marca a imagem com a tag "latest" + docker push $REPO_URI:$IMAGE_TAG # Envia a imagem para o Docker Hub + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8a13706 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Usando a imagem base do Python 3.11 com uma versão slim para reduzir o tamanho da imagem +FROM python:3.11-slim + +# Definindo uma variável de ambiente para a URL do banco de dados +ARG DATABASE_URL +ENV DATABASE_URL=$DATABASE_URL + +# Definindo o diretório de trabalho dentro do container para /app +WORKDIR /app + +# Copiando o arquivo de dependências para dentro do container +COPY requirements.txt . + +# Instalando as dependências a partir do requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Copiando todo o conteúdo do diretório atual para o diretório de trabalho dentro do container +COPY . . + +# Expondo a porta 8000 para a aplicação web (FastAPI) +EXPOSE 8000 + +# Definindo o comando padrão para rodar a aplicação com o Uvicorn, que é um servidor ASGI +CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/api/database.py b/api/database.py new file mode 100644 index 0000000..0c62ae5 --- /dev/null +++ b/api/database.py @@ -0,0 +1,19 @@ +import os +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, declarative_base + +# Obtém a URL de conexão com o banco de dados a partir de uma variável de ambiente +DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///:memory:") + +# Verifica se a variável de ambiente DATABASE_URL não está vazia +if not DATABASE_URL: + raise ValueError("A variável de ambiente DATABASE_URL não está definida!") + +# Cria uma engine de conexão com o banco de dados +engine = create_engine(DATABASE_URL) + +# Configura a sessão do banco de dados (sem commit ou flush automáticos) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Cria a base para os modelos declarativos +Base = declarative_base() diff --git a/api/main.py b/api/main.py new file mode 100644 index 0000000..c216660 --- /dev/null +++ b/api/main.py @@ -0,0 +1,55 @@ +from fastapi import FastAPI, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import List +from api.models import Candidato, Concurso +from api.schemas import CandidatoResponse, ConcursoResponse +from api.database import SessionLocal, engine + +# Inicializa a aplicação FastAPI +app = FastAPI() + +# Função de dependência para obter a sessão do banco de dados +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# Endpoint para listar concursos associados a um CPF +@app.get("/concursos/{cpf}", response_model=List[ConcursoResponse]) +def listar_concursos_por_cpf(cpf: str, db: Session = Depends(get_db)): + candidato = db.query(Candidato).filter(Candidato.cpf == cpf).first() + if not candidato: + raise HTTPException(status_code=404, detail="Candidato não encontrado") + + # Filtra os concursos com base nas profissões do candidato + profissoes_candidato = candidato.profissoes + concursos = db.query(Concurso).all() + concursos_filtrados = [] + + for concurso in concursos: + vagas_concurso = concurso.vagas + if any(profissao in vagas_concurso for profissao in profissoes_candidato): + concursos_filtrados.append(concurso) + + return concursos_filtrados + +# Endpoint para listar candidatos associados a um código de concurso +@app.get("/candidatos/{codigo_concurso}", response_model=List[CandidatoResponse]) +def listar_candidatos_por_concurso(codigo_concurso: str, db: Session = Depends(get_db)): + concurso = db.query(Concurso).filter(Concurso.codigo == codigo_concurso).first() + if not concurso: + raise HTTPException(status_code=404, detail="Concurso não encontrado") + + # Filtra os candidatos com base nas profissões exigidas no concurso + vagas_concurso = concurso.vagas + candidatos = db.query(Candidato).all() + candidatos_filtrados = [] + + for candidato in candidatos: + profissoes_candidato = candidato.profissoes + if any(profissao in vagas_concurso for profissao in profissoes_candidato): + candidatos_filtrados.append(candidato) + + return candidatos_filtrados diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..29cd1e4 --- /dev/null +++ b/api/models.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, Date +from sqlalchemy.dialects.postgresql import JSONB +from api.database import Base + +# Definição da tabela Candidato +class Candidato(Base): + __tablename__ = "candidato" + id = Column(Integer, primary_key=True, index=True) + nome = Column(String(100), nullable=False) + data_nascimento = Column(Date, nullable=False) + cpf = Column(String(14), unique=True, nullable=False) + profissoes = Column(JSONB, nullable=False) + +# Definição da tabela Concurso +class Concurso(Base): + __tablename__ = "concurso" + id = Column(Integer, primary_key=True, index=True) + orgao = Column(String(50), nullable=False) + edital = Column(String(10), nullable=False) + codigo = Column(String(14), unique=True, nullable=False) + vagas = Column(JSONB, nullable=False) diff --git a/api/schemas.py b/api/schemas.py new file mode 100644 index 0000000..a3d03e5 --- /dev/null +++ b/api/schemas.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel, ConfigDict +from typing import List +from datetime import date + +# Definição do schema base para Candidato +class CandidatoBase(BaseModel): + nome: str + data_nascimento: date + cpf: str + profissoes: List[str] + +# Definição do schema base para Concurso +class ConcursoBase(BaseModel): + orgao: str + edital: str + codigo: str + vagas: List[str] + +# Respostas para a API com configurações para Pydantic +class CandidatoResponse(CandidatoBase): + model_config = ConfigDict(from_attributes=True) + +class ConcursoResponse(ConcursoBase): + model_config = ConfigDict(from_attributes=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1d0140c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +fastapi +uvicorn +sqlalchemy +psycopg2-binary +pydantic +pytest +httpx +pytest-cov \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..8aba294 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,97 @@ +# Configuração do provedor AWS, definindo a região onde os recursos serão criados +provider "aws" { + region = var.aws_region +} + +# Criando uma instância EC2 para hospedar o servidor da API +resource "aws_instance" "ledschallenge_api_server" { + ami = "ami-02a53b0d62d37a757" # AMI Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type + instance_type = "t3.micro" # Tipo da instância (t3.micro é uma opção mais barata) + key_name = "terrafomr_create_ec2" # Nome da chave SSH para acesso + security_groups = [aws_security_group.ledschallenge_api_sg.name] # Associando o grupo de segurança + iam_instance_profile = aws_iam_instance_profile.ledschallenge_ec2_role.name # Associando o perfil IAM + + user_data = <<-EOF + #!/bin/bash + # Atualizando pacotes do sistema + sudo yum update -y + + # Instalando o Docker + sudo amazon-linux-extras enable docker + sudo yum install -y docker + + # Iniciando e ativando o Docker + sudo systemctl start docker + sudo systemctl enable docker + + # Adicionando o usuário ec2-user ao grupo docker + sudo usermod -aG docker ec2-user + + # Instalando AWS CLI v2 + sudo yum install -y aws-cli + + # Baixando e rodando o container do Docker Hub + sudo docker pull shubert2/ledschallenge:latest # Substitua pelo seu repositório no Docker Hub + sudo docker run -d -p 8000:8000 --name ledschallenge-api shubert2/ledschallenge:latest + EOF + + tags = { + Name = "LedschallengeAPIInstance" # Definindo uma tag para identificar a instância + } +} + +# Criando o grupo de segurança para a API, permitindo acesso HTTP e SSH +resource "aws_security_group" "ledschallenge_api_sg" { + name = "ledschallenge_api_security_group" + description = "Permitir acesso HTTP e SSH" + + # Regra de entrada para permitir tráfego HTTP (porta 8000) de qualquer IP + ingress { + from_port = 8000 + to_port = 8000 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + # Regra de entrada para permitir tráfego SSH (porta 22) de qualquer IP + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + # Regra de saída permitindo tráfego para qualquer destino + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +# Criando um papel IAM para a instância EC2 +resource "aws_iam_role" "ledschallenge_ec2_role" { + name = "ledschallenge_ec2_role" + + assume_role_policy = < Date: Wed, 26 Feb 2025 13:43:58 -0300 Subject: [PATCH 3/4] update-readme --- README.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index be39a23..1befb3c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Este repositório é uma solução DevOps completa, abrangendo a configuração 4. [GitHub Actions](#github-actions) 5. [Terraform](#terraform) - [Provedor AWS](#provedor-aws) - - [Repositório ECR](#repositório-ecr) - [Instância EC2](#instância-ec2) - [IAM e Políticas](#iam-e-políticas) 6. [Como funciona a API](#como-funciona-a-api) @@ -28,7 +27,7 @@ Antes de começar, verifique se você tem as ferramentas e configurações neces - **Terraform**: Para provisionamento de infraestrutura na AWS. - **AWS CLI**: Para gerenciar e configurar recursos AWS diretamente do seu terminal. - **GitHub Actions**: Para automação do CI/CD. -- **Conta AWS**: Com permissões apropriadas para criar e gerenciar recursos como EC2, IAM, ECR, etc. +- **Conta AWS**: Com permissões apropriadas para criar e gerenciar recursos como EC2, IAM, etc. --- @@ -183,17 +182,7 @@ provider "aws" { } ``` -### 5.2 Repositório ECR - -Configuração para criar um repositório ECR para armazenar as imagens Docker: - -```hcl -resource "aws_ecr_repository" "app_repo" { - name = "ledschallenge-repo" -} -``` - -### 5.3 Instância EC2 +### 5.2 Instância EC2 Provisiona uma instância EC2 para rodar a aplicação: @@ -204,7 +193,7 @@ resource "aws_instance" "app_instance" { } ``` -### 5.4 IAM e Políticas +### 5.3 IAM e Políticas Define as permissões necessárias para interagir com outros recursos da AWS: From 946fccf4d452079e3d575b24a9362064c4cd8809 Mon Sep 17 00:00:00 2001 From: Filipe Suhett <104028395+filipesuhett@users.noreply.github.com> Date: Wed, 26 Feb 2025 14:02:06 -0300 Subject: [PATCH 4/4] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1befb3c..73def1f 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ A conexão com o banco de dados é gerenciada pelo **SQLAlchemy** e a sessão do **Exemplo de Requisição:** ```http -GET /concursos/12345678900 +GET /concursos/123.456.789-00 ``` **Exemplo de Resposta:** @@ -312,4 +312,4 @@ Se você deseja rodar o projeto localmente, siga os passos abaixo: Este repositório tem como objetivo proporcionar uma solução completa e automatizada para o deployment de uma aplicação FastAPI usando práticas de DevOps. Ele integra Terraform para provisionamento de infraestrutura, GitHub Actions para automação de CI/CD e Docker para a criação de ambientes isolados. ---- \ No newline at end of file +---