Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Resolução Desafio Ateliware #92

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0ca06c7
Create Dockerfile
BeppeInfo Jul 22, 2021
d07a989
Create requirements.txt
BeppeInfo Jul 22, 2021
cdd6a05
Create app.py
BeppeInfo Jul 22, 2021
bd3795a
Create docker-compose.yml
BeppeInfo Jul 22, 2021
4378484
Create appspec.yml
BeppeInfo Jul 22, 2021
886580c
Create setup.sh
BeppeInfo Jul 22, 2021
a22a9a3
Update setup.sh
BeppeInfo Jul 22, 2021
2e36467
Create database_init.py
BeppeInfo Jul 22, 2021
aea1a29
Update requirements.txt
BeppeInfo Jul 22, 2021
1b01acc
Update docker-compose.yml
BeppeInfo Jul 22, 2021
343bc84
Create run.sh
BeppeInfo Jul 22, 2021
e6fbab2
Update setup.sh
BeppeInfo Jul 22, 2021
fded715
Update docker-compose.yml
BeppeInfo Jul 22, 2021
b4ed9db
Update docker-compose.yml
BeppeInfo Jul 22, 2021
279a28f
Update requirements.txt
BeppeInfo Jul 22, 2021
0cab307
Update Dockerfile
BeppeInfo Jul 22, 2021
3c7280c
Update Dockerfile
BeppeInfo Jul 22, 2021
f695619
Update requirements.txt
BeppeInfo Jul 22, 2021
30565c1
Update Dockerfile
BeppeInfo Jul 22, 2021
45ad119
Update appspec.yml
BeppeInfo Jul 22, 2021
59948d1
Update setup.sh
BeppeInfo Jul 22, 2021
23530e9
Update appspec.yml
BeppeInfo Jul 22, 2021
5fe8d1a
Update setup.sh
BeppeInfo Jul 22, 2021
63e8eb7
Update docker-compose.yml
BeppeInfo Jul 23, 2021
c23b686
Update Dockerfile
BeppeInfo Jul 23, 2021
bcb73e9
Create deploy.yml
BeppeInfo Jul 23, 2021
ff34965
First functional app
BeppeInfo Jul 24, 2021
7634bf7
Atualização README
BeppeInfo Jul 24, 2021
342c0fa
Change action name
BeppeInfo Jul 24, 2021
a9d8acf
Action test
BeppeInfo Jul 24, 2021
c6ffbfc
Fix CI/CD action
BeppeInfo Jul 24, 2021
f3751da
Action test 2
BeppeInfo Jul 24, 2021
14de856
Fix Dockerfile
BeppeInfo Jul 24, 2021
453894a
Fix database host
BeppeInfo Jul 24, 2021
e8ee569
Fix requirements
BeppeInfo Jul 24, 2021
84b009c
Add shared network to docker-compose
BeppeInfo Jul 24, 2021
d7e8d1e
Docker connection fixes
BeppeInfo Jul 24, 2021
8a1f0c6
Fix setup attempt
BeppeInfo Jul 24, 2021
36f707e
Add initial tests
BeppeInfo Jul 25, 2021
347e25d
Fix deployment action
BeppeInfo Jul 25, 2021
00481cc
Usage instructions
BeppeInfo Jul 25, 2021
07941c1
Local execution instructions
BeppeInfo Jul 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .flaskenv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FLASK_APP='manage.py'
FLASK_ENV=development
APP_SECRET_KEY='\xbb\xe1I\xa0\xb7\xff.\x9f\x04\x0f0\xaf\xcb\x89j8'
DB_USERNAME='postgres'
DB_PASSWORD='postgres'
DB_NAME='flaskapp_db'
31 changes: 31 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI/CD Pipeline

on:
push:
branches: [ master ]

jobs:
continuous-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

continuous-deployment:
runs-on: ubuntu-latest
needs: [continuous-integration]
if: github.ref == 'refs/heads/main'
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Create CodeDeploy Deployment
id: deploy
run: |
aws deploy create-deployment \
--application-name Git_Application \
--deployment-group-name development_group \
--deployment-config-name CodeDeployDefault.OneAtATime \
--github-location repository=${{ github.repository }},commitId=${{ github.sha }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pyc
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3

ENV PYTHONUNBUFFERED=1

RUN mkdir -p /opt/services/flaskapp/src
#VOLUME ["/opt/services/flaskapp/src"]
# We copy the requirements.txt file first to avoid cache invalidations
COPY requirements.txt /opt/services/flaskapp/src/
WORKDIR /opt/services/flaskapp/src
RUN pip install -r requirements.txt
COPY . /opt/services/flaskapp/src
CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"]

218 changes: 202 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,208 @@
# Desafio técnico para desenvolvedores
# Execução Manual

Construa uma nova aplicação, utilizando o framework de sua preferência (Ruby on Rails, Elixir Phoenix, Python Django ou Flask, NodeJS Sails, Java Spring, ASP.NET ou outro), a qual deverá conectar na API do GitHub e disponibilizar as seguintes funcionalidades:
### Dependências
***

- Botão para buscar e armazenar os repositórios destaques de 5 linguagens à sua escolha;
- Listar os repositórios encontrados;
- Visualizar os detalhes de cada repositório.
Para executar a aplicação localmente, tenha um interpretador **Python 3.x** instalado e adicione os pacotes necessários com o comando:

Alguns requisitos:
```shell
pip install -r requirements.txt
```

- Deve ser uma aplicação totalmente nova;
- A solução deve estar em um repositório público do GitHub;
- A aplicação deve armazenar as informações encontradas;
- Utilizar PostgreSQL, MySQL ou SQL Server;
- O deploy deve ser realizado, preferencialmente, no Heroku, AWS ou no Azure;
- A aplicação precisa ter testes automatizados;
- Preferenciamente dockerizar a aplicação;
- Por favor atualizar o readme da aplicação com passo a passo com instrução para subir o ambiente.
***
### Ambiente
***

Quando terminar, faça um Pull Request neste repo e avise-nos por email.
Antes de rodar a aplicação, tenha uma base de dados **PostgreSQL** disponível de acordo com as configurações indicadas no arquivo `.flaskenv`

**IMPORTANTE:** se você não conseguir finalizar o teste, por favor nos diga o motivo e descreva quais foram as suas dificuldades. Você pode também sugerir uma outra abordagem para avaliarmos seus skills técnicos, vender seu peixe, mostrar-nos do que é capaz.
Inicialize as tabelas do banco executando a migration já configurada:

```shell
flask db upgrade
```

Após isso é possível iniciar a aplicação, definindo ou não o endereço do banco de dados com a variável _DB_HOST_ (valor padrão **'localhost'**), com o comando:

```shell
DB_HOST=<your_host> flask run
```
***
### Testes
***

Testes podem ser executados com o comando:

```shell
DB_HOST=<your_test_host> python test_runner.py
```

# Execução com Docker

Para inicializar banco de dados e aplicação com **docker-compose**, execute os seguintes comandos no shell:

```shell
docker-compose up -d db
```
```shell
docker-compose build --no-cache
```
```shell
docker-compose run --rm flaskapp flask db upgrade
```
```shell
docker-compose up -d
```

# Deployment Automático em Instância Amazon EC2

### Crie os IAM Roles necessários
***

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26bulqgskw5bpu64r0kl.png)

Um para a instância **EC2**
- _Trusted entity_: **AWS Service**
- _Use Case_: **EC2**
- _Policies_: **AmazonEC2RoleforAWSCodeDeploy**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46kuzgo4qxpc639girdl.png)

- Abra as configurações do _Role_ criado e edite a _Trust Relationship_ da forma abaixo:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irtiqqgvv9uig7zxlist.png)

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
```

Outro para o **CodeDeploy**
- _Trusted entity_: **AWS Service**
- _Use Case_: **EC2**
- _Policies_: **AmazonEC2FullAccess**, **AWSCodeDeployFullAccess**, **AdministratorAccess**, **AWSCodeDeployRole**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y6l3k4yjo76yvbwu2ut1.png)

- Abra as configurações do _Role_ criado e edite a _Trust Relationship_ da forma abaixo:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
```

***
### Crie Instância EC2
***

Para testes, foi utilizada uma máquina **Amazon Linux 2 AMI (HVM), SSD Volume Type** com arquitetura **64-bit (x86)** do tipo **t2.micro**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsbfckiuolr4y1oj5ilt.png)

Para estabelecer a conexão entre a instância **EC2** e o CodeDeploy, selecione o primeiro _IAM Role_ criado para as configurações da máquina.

Na página de _Tags_, add a tag called **development**. The tag will require creating a _codeDeploy_ service.

Em _Configure Security Group_, por conveniência, configure o _Type_ para **All traffic** e _Source_ para **Anywhere** (Não recomendado para produção)

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c66iy10uzgmv6dygz5cl.png)

Inicialize a instância e espere alguns minutos

***
### Instale o CodeDeploy Agent na Instância EC2
***

Quando a instância começar a ser executada, conecte-se a ela usando _User name_ padrão

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wd9853dwfnc7hldoy7n9.png)

Use os seguintes comandos para instalar o **codedeploy-agent**.

```shell
sudo yum update
```

```shell
sudo yum install -y ruby
```

```shell
sudo yum install wget
```

```shell
wget https://<bucket-name>.s3.<region-identifier>.amazonaws.com/latest/install
```
> Substitua **bucket-name** e **region-identifier** pelos [valores correspondentes à sua região](https://docs.aws.amazon.com/codedeploy/latest/userguide/resource-kit.html#resource-kit-bucket-names)

```shell
chmod +x ./install
```

```shell
sudo ./install auto
```

```shell
sudo service codedeploy-agent start
```

***
### Configure Serviço CodeDeploy
***

Crie uma _Application_ de nome **Git_Application** com _Compute platform_ **EC2/On-premises**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hciskx6rrwq0r4m3whw6.png)

Em seguida, cria um _Deployment Group_ com nome **development_group**. Para o _Service role_, utilize o _Role ARN_ do **CodeDeploy_Role** criado anteriormente

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqw1plmd8tpjscukpr7d.png)

Escolha **In-place** para _Deployment type_. Selecione **Amazon Ec2 Instances** em _Environment configuration_ e adicione a _Tag key_ **development**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41sxab2cuhni6412y7f5.png)

Defina **OneAtATime** em _Deployment settings_ e desabilite **Enable load balancing**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xivhirkebhcrl9ne7kb.png)

Com o _Deployment Group_ criado, configure um _Deployment_ com _Revision Type_ **My application is stored in GitHub**, ative **Connect to GitHub** após preencher seu [GitHub token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) e forneça o nome de seu repositório (**AnCaPepe/dev-hiring-challenge** ou seu fork) em _Repository name_ e o _Commit ID_ da revisão a ser utilizada

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3s5f3cv3auw4magmt33c.png)

Selecione **Overwrite the content** em _Content options_ e termine a criação do _Deployment_

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qya9yce6v5zxb09c21um.png)

Aguarde o fim do processo e se houver alguma falha, verifique os logs da instância **EC2** em `/var/log/aws/codedeploy-agent/codedeploy-agent.log`.

***
### Configure GitHub Actions
***

Para realizar o deployment automático após mudanças em seu [fork](https://docs.github.com/en/github/getting-started-with-github/quickstart/fork-a-repo) do repositório, crie um [IAM user](https://docs.amazonaws.cn/en_us/IAM/latest/UserGuide/id_users_create.html#id_users_create_console) com _policy_ **AWSCodeDeployFullAccess** e gere uma [access key e secret access](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey) para o usuário

Defina as variáveis de ambiente do repositório **GitHub** com as informações de acesso e região

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkmvr6y8pi9aqpdjlj6b.png)
23 changes: 23 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()

def create_app(**config_overrides):
app = Flask(__name__)

app.config.from_pyfile( 'settings.py' )
app.config.update( config_overrides )

db.init_app( app )
migrate = Migrate( app, db )

from controllers.repository_search import repositories
app.register_blueprint( repositories )

return app

if __name__ == "__main__":
app = create_app()
app.run( host='0.0.0.0', port=5000 )
14 changes: 14 additions & 0 deletions appspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 0.0
os: linux
files:
- source: .
destination: /home/ec2-user/ateliware-challenge/
hooks:
AfterInstall:
- location: setup.sh
timeout: 300
runas: root
ApplicationStart:
- location: run.sh
timeout: 300
runas: root
48 changes: 48 additions & 0 deletions controllers/repository_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from flask import Blueprint, render_template, request, redirect, url_for, session
from urllib.request import urlopen
from urllib.parse import urlencode
from urllib.error import HTTPError
import json

from models.repository import Repository
from app import db

LANGS_NUMBER = 5

lang_keys = [ f'lang_{i}' for i in range(0, LANGS_NUMBER) ]
repositories = Blueprint( 'repositories', __name__ )

results = []

@repositories.route( '/' )
@repositories.route( '/search', methods=[ 'GET', 'POST' ] )
def enter_search():
if request.method == 'POST':
for key in lang_keys:
session[ key ] = request.form.get( key )
return redirect( url_for( 'repositories.show_results' ) )
return render_template( 'search.html', lang_keys=lang_keys )

@repositories.route( '/results' )
def show_results():
results = []
for key in lang_keys:
lang = session.get( key )
params = urlencode( { 'q':f'language:{lang}', 'sort':'stars', 'order':'desc' } )
try:
with urlopen( 'https://api.github.com/search/repositories?' + params ) as response:
data = json.load( response )['items'][0]
result = Repository.query.filter( Repository.lang==lang ).first()
if not result:
result = Repository( lang, data['full_name'], data['description'], data['html_url'], data['watchers'] )
db.session.add( result )
else:
result.name = data['full_name']
result.description = data['description']
result.link = data['html_url']
result.stars = data['watchers']
results.append( result )
db.session.commit()
except HTTPError as e:
print( e )
return render_template( 'results.html', results=results )
Loading