Saltar al contenido principal

Clase 06 — APIs en la Práctica: Servicios Reales

APIs que todo DevOps debe conocer

1. GitHub API

GH="https://api.github.com"
AUTH=(-H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json")

# Tu perfil
curl -s "${AUTH[@]}" "$GH/user" | jq '{login, name, public_repos, followers}'

# Crear repo
curl -s -X POST "${AUTH[@]}" "$GH/user/repos" \
-d '{"name":"test-api","private":true,"auto_init":true}' | jq '.html_url'

# Crear issue
curl -s -X POST "${AUTH[@]}" "$GH/repos/OWNER/REPO/issues" \
-d '{"title":"Bug via API","body":"Descripción","labels":["bug"]}' | jq '.html_url'

# Trigger GitHub Actions workflow
curl -s -X POST "${AUTH[@]}" \
"$GH/repos/OWNER/REPO/actions/workflows/deploy.yml/dispatches" \
-d '{"ref":"main","inputs":{"environment":"staging"}}'

# Listar workflow runs
curl -s "${AUTH[@]}" "$GH/repos/OWNER/REPO/actions/runs?per_page=5" | \
jq '.workflow_runs[] | {id, name, status, conclusion, created_at}'

2. Docker Hub / Registry API

# Obtener token para imagen pública
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/nginx:pull" | jq -r '.token')

# Listar tags
curl -s -H "Authorization: Bearer $TOKEN" \
"https://registry-1.docker.io/v2/library/nginx/tags/list" | jq '.tags[:10]'

# Docker socket API (local)
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | \
jq '.[] | {id: .Id[0:12], name: .Names[0], status: .Status, image: .Image}'

3. Kubernetes API

# Desde dentro de un pod
K8S="https://kubernetes.default.svc"
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

curl -s --cacert "$CA" -H "Authorization: Bearer $TOKEN" \
"$K8S/api/v1/namespaces/default/pods" | jq '.items[].metadata.name'

# Desde fuera (con kubectl proxy)
kubectl proxy &
PROXY="http://localhost:8001"

curl -s "$PROXY/api/v1/namespaces" | jq '[.items[].metadata.name]'

curl -s "$PROXY/apis/apps/v1/namespaces/default/deployments" | \
jq '.items[] | {name: .metadata.name, replicas: .spec.replicas, ready: .status.readyReplicas}'

# Escalar un deployment
curl -s -X PATCH "$PROXY/apis/apps/v1/namespaces/default/deployments/mi-app" \
-H "Content-Type: application/merge-patch+json" \
-d '{"spec":{"replicas":5}}' | jq '.spec.replicas'

4. Terraform Cloud API

TF="https://app.terraform.io/api/v2"
TF_AUTH=(-H "Authorization: Bearer $TFC_TOKEN" -H "Content-Type: application/vnd.api+json")

curl -s "${TF_AUTH[@]}" "$TF/organizations/mi-org/workspaces" | \
jq '.data[] | {name: .attributes.name, version: .attributes."terraform-version"}'

Webhooks — APIs inversas

Los webhooks son "APIs al revés": el servidor te envía datos a vos cuando ocurre un evento.

API normal:     Vos → Petición → Servidor → Respuesta → Vos
Webhook: Evento → Servidor → POST → Tu endpoint → Procesás

Recibir webhooks con FastAPI

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook/github")
async def github_webhook(request: Request):
event = request.headers.get("X-GitHub-Event", "unknown")
payload = await request.json()

if event == "push":
repo = payload["repository"]["full_name"]
branch = payload["ref"].split("/")[-1]
commits = len(payload.get("commits", []))
print(f"🔔 Push a {repo}/{branch}: {commits} commits")

elif event == "pull_request":
action = payload["action"]
pr = payload["pull_request"]
print(f"🔔 PR #{pr['number']} {action}: {pr['title']}")

return {"status": "received", "event": event}

Enviar webhooks

# Slack
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d '{"text": "🚀 Deploy v2.0 completado en producción"}'

# Discord
curl -s -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d '{"content": "Deploy exitoso", "embeds": [{"title": "v2.0", "color": 3066993}]}'

APIs GraphQL (introducción)

# GraphQL usa un solo endpoint y POST
curl -s -X POST https://api.github.com/graphql \
-H "Authorization: bearer $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query { viewer { login name repositories(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) { nodes { name stargazerCount } } } }"
}' | jq '.data'

REST vs GraphQL

AspectoRESTGraphQL
EndpointsMúltiples (/users, /posts)Uno solo (/graphql)
DatosRespuesta fija del servidorCliente pide exactamente lo que necesita
Over-fetchingComúnNo (solo lo que pedís)
CacheFácil (por URL)Complejo
DocumentaciónOpenAPI/SwaggerSchema introspection

Ejemplo: Monitor multi-servicio

#!/bin/bash
check_endpoint() {
local nombre="$1" url="$2" expected="$3"
local start http_code duration

start=$(date +%s%N)
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url")
duration=$(( ($(date +%s%N) - start) / 1000000 ))

if [[ "$http_code" == "$expected" ]]; then
printf " ✅ %-25s HTTP %-4s %4dms\n" "$nombre" "$http_code" "$duration"
else
printf " ❌ %-25s HTTP %-4s (esperado: %s)\n" "$nombre" "$http_code" "$expected"
fi
}

echo "API Infrastructure Monitor — $(date '+%Y-%m-%d %H:%M:%S')"
echo "══════════════════════════════════════════════"
check_endpoint "GitHub API" "https://api.github.com" "200"
check_endpoint "PokéAPI" "https://pokeapi.co/api/v2/pokemon/1" "200"
check_endpoint "JSONPlaceholder" "https://jsonplaceholder.typicode.com/posts/1" "200"
check_endpoint "httpbin" "https://httpbin.org/get" "200"

Ejercicios

  1. Usá la GitHub API para: crear un repo, agregar un archivo, crear un issue
  2. Consultá la Docker Hub API para listar tags de nginx y python
  3. Creá un webhook receiver con FastAPI que procese eventos de push
  4. Escribí un monitor que verifique 5 APIs y envíe alertas a Slack si alguna falla
  5. Hacé una query GraphQL a la GitHub API para obtener info de un repo con sus issues