├── README.md ├── docker-compose.yaml └── services ├── frontend ├── Dockerfile ├── css │ └── styles.css ├── index.html ├── js │ └── app.js └── package.json ├── reader ├── Dockerfile └── main.go └── writer ├── Dockerfile └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # Desafio DevOps jr PicPay 2 | 3 | Obrigado pelo interesse em fazer parte do nosso time! Preparamos este desafio com carinho para ajudar a entender um pouco mais dos seus conhecimentos na área de DevOps/SRE 4 | 5 | Se não entender algum conceito ou parte do problema, não é motivo para se preocupar! Queremos que faça o desafio até onde souber. 6 | 7 | No mais, divirta-se :D 8 | 9 | ## Conteúdo do repositório 10 | Na pasta `services` deste repositório existem 3 aplicações, um frontend que se comunica com um backend go e um em python, e estes se comunicam com um Redis para troca de informações. Tudo isso é orquestrado pelo docker-compose na raiz do repositório. 11 | 12 | As aplicações contém falhas propositais, de código, projeto, imagem docker, etc. Embora cada aplicação funcione individualmente, o conjunto não sobe... 13 | 14 | ## O que deve ser feito? 15 | 16 | Faça um fork deste repositório e envie uma pull request contendo: 17 | - ajustes que fazem todas as aplicações subirem e se comunicarem 18 | - um README contendo os seus pensamentos ao longo do projeto 19 | - um desenho contendo os serviços que explique o funcionamento 20 | 21 | Faça commits ao longo do processo, queremos entender o seu modo de pensar! :) 22 | 23 | Para a entrevista, separe também anotações contendo melhorias que faria em cada aplicação e o motivo. Não envie estas anotações na pull request. 24 | 25 | ## Bibliografia recomendada 26 | https://docs.docker.com/engine/reference/builder/ 27 | 28 | https://docs.docker.com/compose/compose-file/ 29 | 30 | https://12factor.net/ 31 | 32 | https://conventionalcommits.org/ -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | web: 4 | build: services/frontend 5 | ports: 6 | - "5000:5000" 7 | networks: 8 | - frontend 9 | - backend 10 | reader: 11 | build: services/reader 12 | ports: 13 | - "8081:8081" 14 | networks: 15 | - frontend 16 | writer: 17 | build: services/writer 18 | ports: 19 | - "8080:8080" 20 | networks: 21 | - backend 22 | reids: 23 | image: "redis:alpine" 24 | 25 | networks: 26 | backend: 27 | -------------------------------------------------------------------------------- /services/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | WORKDIR /app 3 | ADD . . 4 | RUN npm install -g serve 5 | EXPOSE 5000 6 | CMD "serve" -------------------------------------------------------------------------------- /services/frontend/css/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PicPay/picpay-jr-devops-challenge/8dc4484dd0360f7ec43f6d37e5d14923c219dc9b/services/frontend/css/styles.css -------------------------------------------------------------------------------- /services/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 29 |
30 | 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /services/frontend/js/app.js: -------------------------------------------------------------------------------- 1 | const root = document.querySelector("#root") 2 | 3 | const home = () => { 4 | const container = document.createElement('div'); 5 | container.innerHTML = ` 6 |

Home

7 | ` 8 | 9 | const readerp = container.appendChild(document.createElement('p')) 10 | const writerp = container.appendChild(document.createElement('p')) 11 | readerhealth(readerp) 12 | writerhealth(writerp) 13 | container.classList.add("mx-auto","align-middle","container") 14 | 15 | return container; 16 | }; 17 | 18 | const reader = () => { 19 | const container = document.createElement('div'); 20 | container.innerHTML = ` 21 |

Reader Screen

22 | ` 23 | const child = container.appendChild(document.createElement('p')) 24 | readData(child) 25 | container.classList.add("mx-auto","align-middle","container") 26 | 27 | return container; 28 | }; 29 | 30 | const writer = () => { 31 | const container = document.createElement('div'); 32 | container.innerHTML = ` 33 |

Writer Screen

34 |
35 |
36 | 37 | 38 |
39 | 40 |
41 | ` 42 | container.classList.add("mx-auto","align-middle","container") 43 | const formsubmit = container.querySelector("#submitdata") 44 | formsubmit.addEventListener('submit',writeData) 45 | return container; 46 | } 47 | 48 | const readerhealth = (readerparagraph) => { 49 | const url='http://localhost:8080/health'; 50 | fetch(url, { 51 | method: "GET", 52 | mode: 'cors', 53 | headers: { 54 | 'Access-Control-Allow-Origin': '*' 55 | }, 56 | }).then( response => { 57 | response.text().then(body => { 58 | assembleStatus(readerparagraph, "Reader", body) 59 | }).catch(err => { 60 | assembleStatus(readerparagraph, "Reader", "down") 61 | }) 62 | }) 63 | .catch( error => { 64 | console.log(error) 65 | assembleStatus(readerparagraph,"Reader","down") 66 | }) 67 | } 68 | 69 | const writerhealth = (writerparagraph) => { 70 | const url='http://localhost:8081/health'; 71 | fetch(url, { 72 | method: "GET", 73 | mode: 'cors', 74 | headers: { 75 | 'Access-Control-Allow-Origin': '*' 76 | }, 77 | }).then( response => { 78 | response.text().then(body => { 79 | assembleStatus(writerparagraph, "Writer", body) 80 | }).catch(err => { 81 | assembleStatus(writerparagraph, "Writer", "down") 82 | }) 83 | }).catch( error => { 84 | assembleStatus(writerparagraph,"Writer","down") 85 | }) 86 | } 87 | 88 | const assembleStatus = (paragraph, service, status) => { 89 | let statusIcon = 'danger' 90 | let statusBadge = 'danger' 91 | if (status == "up") { 92 | statusIcon = 'success' 93 | statusBadge = 'success' 94 | } 95 | paragraph.innerHTML= 96 | service + ' service status '+status+'' 97 | } 98 | 99 | async function writeData(e) { 100 | e.preventDefault(); 101 | 102 | const url='http://localhost:8081/write'; 103 | fetch(url, { 104 | method: "POST", 105 | body: e.target.elements.post.value, 106 | mode: 'cors', 107 | headers: { 108 | 'Access-Control-Allow-Origin': '*' 109 | }, 110 | }).then( response => { 111 | 112 | }).catch( error => { 113 | console.log(error) 114 | }) 115 | } 116 | 117 | const readData = (paragraph) => { 118 | const url='http://localhost:8080/data'; 119 | fetch(url, { 120 | method: "GET", 121 | mode: 'cors', 122 | headers: { 123 | 'Access-Control-Allow-Origin': '*' 124 | }, 125 | }).then( response => { 126 | response.text().then(body => { 127 | paragraph.innerHTML = "Valor encontrado = "+body 128 | }).catch(err => { 129 | paragraph.innerHTML = ` 130 | 133 | ` 134 | }) 135 | }) 136 | .catch( error => { 137 | console.log(error) 138 | }) 139 | } 140 | 141 | 142 | const routes = { 143 | home: home(), 144 | reader: reader(), 145 | writer: writer(), 146 | } 147 | 148 | const validateHash = (hash) => hash === "" ? 'home' : hash.replace('#', ''); 149 | const init = () => window.addEventListener('hashchange', renderPage); 150 | 151 | const renderPage = () => { 152 | const page = validateHash(window.location.hash); 153 | root.innerHTML = ''; 154 | root.appendChild(routes[page]) 155 | } 156 | 157 | window.addEventListener('load', ()=> { 158 | renderPage(); 159 | init(); 160 | }); -------------------------------------------------------------------------------- /services/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spa", 3 | "scripts": { 4 | "start": "serve ./" 5 | }, 6 | "dependencies": { 7 | "serve": "^11.3.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /services/reader/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16 2 | WORKDIR /go/src/github.com/PicPay/picpay-jr-devops-challenge/services/go 3 | ADD . /go/src/github.com/PicPay/picpay-jr-devops-challenge/services/go 4 | 5 | EXPOSE 8080 6 | CMD ["go","run","main.go"] -------------------------------------------------------------------------------- /services/reader/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-redis/redis" 6 | "github.com/rs/cors" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | 12 | func main() { 13 | redis_host := "redis" 14 | redis_port := "6379" 15 | mux := http.NewServeMux() 16 | 17 | mux.HandleFunc("/health", func(writer http.ResponseWriter, request *http.Request){ 18 | if request.Method == "OPTIONS" { 19 | writer.WriteHeader(http.StatusOK) 20 | return 21 | } 22 | fmt.Fprintf(writer, "up") 23 | }) 24 | 25 | mux.HandleFunc("/data", func(writer http.ResponseWriter, request *http.Request) { 26 | client := redis.NewClient(&redis.Options{Addr: redis_host+":"+redis_port}) 27 | key := client.Get(client.Context(),"SHAREDKEY") 28 | fmt.Fprintf(writer, key.Val()) 29 | }) 30 | 31 | handler := cors.New(cors.Options{ 32 | AllowedOrigins: []string{"*"}, 33 | AllowedHeaders: []string{"*"}, 34 | }).Handler(mux) 35 | 36 | log.Fatal(http.ListenAndServe(":8080", handler)) 37 | 38 | } -------------------------------------------------------------------------------- /services/writer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | WORKDIR /app 3 | ADD . /app 4 | EXPOSE 8081 5 | CMD ["python"] 6 | ENTRYPOINT ["main.py"] -------------------------------------------------------------------------------- /services/writer/main.py: -------------------------------------------------------------------------------- 1 | import http.server 2 | import socketserver 3 | import json 4 | import redis 5 | 6 | class RequestHandler(http.server.SimpleHTTPRequestHandler): 7 | 8 | def _send_cors_headers(self): 9 | self.send_header('Access-Control-Allow-Origin', '*') 10 | self.send_header('Access-Control-Allow-Methods', '*') 11 | self.send_header('Access-Control-Allow-Headers', '*') 12 | 13 | def do_OPTIONS(self): 14 | self.send_response(200, "ok") 15 | self._send_cors_headers() 16 | self.end_headers() 17 | 18 | def do_GET(self): 19 | self.send_response(200) 20 | self.send_header("Content-type", "application/json") 21 | self._send_cors_headers() 22 | self.end_headers() 23 | 24 | if self.path == '/health': 25 | self.wfile.write(bytes(f"up", "utf8")) 26 | 27 | return 28 | def do_POST(self): 29 | self.send_response(201) 30 | self.send_header("Content-type", "application/json") 31 | self._send_cors_headers() 32 | self.end_headers() 33 | if self.path == '/write': 34 | content_length = int(self.headers['Content-Length']) 35 | post_data = self.rfile.read(content_length) 36 | redishost = "redis" 37 | redisclient = redis.Redis(host=redishost) 38 | redisclient.set("SHAREDKEY",post_data.decode('utf-8')) 39 | 40 | 41 | 42 | handler_object = RequestHandler 43 | server = socketserver.TCPServer(("", 8081), handler_object) 44 | server.serve_forever() --------------------------------------------------------------------------------