# Tema 5 · 4 · Ejercicio 2, tipo 2: CoinGecko a DynamoDB Presentación del Tema 5: «Ejercicio 2: CoinGecko (Tipo 2)» y «Ejercicio 2 · programar la ejecución con EventBridge». Duración: 25-30 min (sin contar EventBridge). Coste: prácticamente nulo (Lambda y DynamoDB en modo bajo demanda con este volumen). Esta actividad **no necesita la infraestructura base** (carpeta 05.01), así que no genera coste de RDS ni EC2. > **Solo haces UNA variante del Ejercicio 2.** La carpeta 05.03 (S3), esta (DynamoDB) y la 05.05 (S3 + RDS) son las tres variantes. Haz la que te asigne tu docente. No hagas las tres. > **¿Dudas con un script?** Todos los scripts de esta carpeta traen su propia ayuda: `./gestionar-crypto-dynamodb.sh --ayuda` (también vale `--help` o `-h`) explica los pasos y las opciones. Este `README.md` es el paso a paso de la actividad: tenlo a mano y consúltalo antes de preguntar. > **Etiquetas de lo que creas.** `./gestionar-crypto-dynamodb.sh crear` etiqueta el stack (Lambda, tabla, rol) y la regla de EventBridge con `curso` = `G214`, `universidad` = `CUNEF`, `actividad` = `05.04-cryptoadynamodb` y `script` = `gestionar-crypto-dynamodb.sh`. Las verás en la consola (pestaña *Tags* del recurso) y, si hiciera falta borrar a mano, se localizan con `aws resourcegroupstaggingapi get-resources --region eu-north-1 --tag-filters Key=actividad,Values=05.04-cryptoadynamodb`. Más detalle (cómo etiquetar con la CLI, cómo buscar y qué hacer en una emergencia): [README principal](../../README.md), sección «Etiquetas». ## Qué vas a hacer Vas a crear una función Lambda que pide a **CoinGecko** (API pública, sin clave) el precio en USD y EUR de cinco monedas (`bitcoin`, `ethereum`, `cardano`, `solana`, `polkadot`) con su capitalización, volumen y variación en 24 horas, y guarda un ítem por moneda en una tabla de **DynamoDB**. **Qué se crea en AWS:** el stack `cunef-etl-crypto-dynamodb` (tabla DynamoDB `cunef-etl-demo-crypto-prices` + rol + función `crypto-price-tracker-dynamodb`, 30 s, 128 MB). Lo crea, lo comprueba y lo borra **un único script**: `gestionar-crypto-dynamodb.sh`. **Qué hace la función:** escribe un ítem por moneda en la tabla `cunef-etl-demo-crypto-prices` (clave de partición `crypto_name`, por ejemplo `BITCOIN`; clave de ordenación `timestamp`, con formato `AAAA-MM-DD HH:MM:SS`). Cada ítem lleva `price_usd`, `price_eur`, `market_cap_usd`, `volume_24h_usd`, `change_24h_percent`, `trend` (`UP` o `DOWN`) y otros campos. DynamoDB no admite decimales de tipo `float`, por eso el código usa `Decimal`. El nombre de la tabla ya coincide con el de la plantilla: no hay que editar nada. ## Lo que debes saber antes de empezar (vale para las tres variantes) 1. **Un solo script hace todo.** `./gestionar-crypto-dynamodb.sh crear` crea el stack de CloudFormation (tabla + rol + función, con la plantilla que lleva dentro), sube el código de la función y, si quieres, la prueba. **Ya no hay que crear el stack a mano.** Es seguro repetirlo: si todo existe, solo actualiza el código; si quedó algo a medias de un intento anterior, lo repara. 2. **Menú o comandos.** Sin argumentos y en una terminal, el script muestra un menú numérico. Con argumentos ejecuta directamente un comando: `crear`, `estado`, `probar`, `logs`, `programar`, `extender`, `desprogramar`, `eliminar`, `extraer` y `ayuda` (lista completa en *Los comandos del script*). 3. **El script pregunta** `¿Deseas probar la función ahora? (s/n):` al terminar `crear`. Escribe `s` y pulsa Intro. Con `n` solo despliega. Para no tener que contestar: `crear --si` (prueba) o `crear --no` (no prueba). Sin terminal interactiva nunca se queda esperando: no prueba. 4. **El script ya no crea un rol de IAM propio.** La función usa el rol del stack. Si en una versión anterior se te creó un rol sobrante (`LambdaCryptoDynamoDBRole`), `eliminar` lo borra. 5. **El script marca el resultado con el `statusCode` de la función.** Tras la prueba verás `✓ Función ejecutada correctamente (statusCode 200)` si fue bien, o `✗ La función devolvió statusCode 500` seguido del motivo (campo `error`) si falló por dentro. Con el CLI a mano ocurre lo contrario: `"StatusCode": 200` solo dice que la llamada de invocación terminó, aunque dentro vaya `statusCode` 500. Comprueba siempre el dato **en el destino**. 6. **Cada ejecución añade datos nuevos** (no se sobrescribe lo anterior). Repetir la invocación es seguro, pero acumula ítems. 7. **Los "Comandos útiles" que imprime el script** ya llevan `--cli-binary-format raw-in-base64-out` (AWS CLI v2). 8. **CoinGecko limita las peticiones y el límite es bajo.** Tras unas 5 peticiones seguidas desde la misma IP responde error 429 (`Status 429` o `HTTP Error 429` en el campo `error`; en una prueba, 5 respuestas 200 y 7 con 429 en una ráfaga de 12). No encadenes el `curl` de comprobación, el script con `s`, el `invoke` a mano y la regla de cada minuto: deja 1-2 minutos entre pruebas. Si toda la clase sale por la misma IP, el 429 aparecerá antes. No es culpa de tu código: espera uno o dos minutos y vuelve a invocar. La función no reintenta sola. Qué deberías ver tras la **primera** ejecución correcta: **5 ítems en DynamoDB** (uno por moneda); en cada ejecución siguiente, 5 ítems más. ## Qué contiene esta carpeta | Fichero | Para qué sirve | En qué paso se usa | |---|---|---| | `gestionar-crypto-dynamodb.sh` | **Todo el ejercicio en un solo archivo:** crea el stack (tabla, rol y función) y sube el código, comprueba el estado, prueba la función, programa EventBridge y lo elimina todo. Lleva dentro el código de la Lambda y la plantilla de CloudFormation. | Pasos 2, 3, 4 y 5, y la Limpieza | | `README.md` | Este documento. | Siempre | **El código de la función y la plantilla viajan dentro del script.** Para verlos o editarlos, extráelos a la carpeta actual: ```bash ./gestionar-crypto-dynamodb.sh extraer ``` Esto escribe `lambda-crypto-dynamodb.py` (el código de la función) y `plantilla-crypto-dynamodb.yaml` (la plantilla de CloudFormation). **No son necesarios para desplegar.** En este ejercicio no tienes que editar nada, pero si editas el `.py` y ejecutas `crear`, el script despliega **tu** versión (también detecta un `lambda_function.py` en la carpeta, o el fichero que le indiques con `--codigo mi-codigo.py`; si no compila, no despliega nada y te lo dice). La plantilla extraída solo se usa con `crear --plantilla plantilla-crypto-dynamodb.yaml`. Si el fichero ya existe, `extraer` no lo sobrescribe (`extraer --forzar` lo hace y guarda una copia `.bak`). ## Antes de empezar - **No depende de la carpeta 05.01** (tiene su propia tabla). Solo necesitas una cuenta de AWS con permisos para crear recursos. - Región **eu-north-1 (Estocolmo)** seleccionada en la consola (el script ya trabaja en esa región). - CloudShell (trae `aws`, `zip` y `python3`). No hace falta `jq`. ## Cómo llevar los ficheros a CloudShell 1. En tu PC, comprime **esta carpeta entera** (`05.04-cryptoadynamodb`): clic derecho → *Comprimir en archivo ZIP* (Windows 11), *Enviar a > Carpeta comprimida en zip* (Windows 10) o *Comprimir* (Mac). 2. En CloudShell (región eu-north-1): **Actions → Upload file** y elige `05.04-cryptoadynamodb.zip`. 3. Ejecuta: ```bash cd ~ unzip -o 05.04-cryptoadynamodb.zip && cd 05.04-cryptoadynamodb chmod -R u+rwX . && chmod +x *.sh ls ``` > Si ya subiste `g214-materiales.zip` (README principal), no necesitas este zip: entra con `cd ~/g214-materiales/tema5/05.04-cryptoadynamodb` y, si algún fichero da `Permission denied`, ejecuta allí `chmod -R u+rwX . && chmod +x *.sh`. **Qué verás:** `gestionar-crypto-dynamodb.sh` y `README.md`. Ejecuta el script siempre **dentro de esta carpeta**. ## Los comandos del script `./gestionar-crypto-dynamodb.sh COMANDO [opciones]` (`ayuda` los lista; sin comando y con terminal, abre el menú). | Comando (alias) | Qué hace | |---|---| | `crear` (`desplegar`, `deploy`) | Crea o actualiza el stack (tabla, rol, función), sube el código y, si quieres, prueba la función. Repetible. | | `estado` (`status`, `verificar`) | Muestra qué existe (stack, función, rol, tabla con su número de ítems, reglas, logs), la última ejecución, las últimas líneas del log y **qué hacer a continuación**. | | `probar` (`test`, `invocar`) | Ejecuta la función una vez y comprueba `statusCode` y los ítems de la tabla. | | `logs` | Últimas líneas de los logs (`logs --seguir`: en directo). | | `programar [N]` (`eventbridge`) | Ejecuta la función cada N minutos (por defecto 1) con EventBridge **durante 4 h**: después la regla se desactiva sola (ver *Apagado automático*). Si la regla ya existe, la reactiva y renueva el plazo. Ver [05.06](../05.06-ProgramarEventBridge.md). | | `extender` (`ampliar`) | Solo cambia el plazo del apagado automático (otras 4 h; `--horas N` o `--permanente`). | | `desprogramar` (`parar`) | Quita (borra) la programación. | | `eliminar` (`borrar`, `limpiar`) | **Borra todo lo de este ejercicio** (incluida la tabla y sus datos) y comprueba al final qué queda. Pide confirmación (con `--si` o `-y` no). | | `extraer` | Escribe en la carpeta el `.py` y el `.yaml` embebidos para verlos o editarlos. | Opciones: `--si`/`-y` (responde sí: `crear` prueba la función, `eliminar` no pregunta), `--no` (`crear` no prueba; `eliminar` no borra), `--codigo F.py`, `--plantilla F.yaml`, `--forzar` (con `extraer`), `--seguir` (con `logs`), `--cada "rate(1 hour)"`, `--regla NOMBRE`, `--horas N` (1-24) y `--permanente` (con `programar` y `extender`). ## Paso a paso **1. Comprueba que CoinGecko responde desde CloudShell:** ```bash curl -s -o /dev/null -w "%{http_code}\n" "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=eur" ``` **Qué verás:** `200`. Si ves `429` u otro número, la API te está limitando o no responde: espera un poco y repite antes de seguir. **2. Crea todo y despliega el código:** ```bash ./gestionar-crypto-dynamodb.sh crear ``` **Qué verás, en orden** *(los textos pueden variar ligeramente)*: "Verificando prerequisitos"; un aviso de que este ejercicio no necesita la infraestructura base; "Preparando el código y la plantilla" (te dice si usa el código embebido o **tu** fichero); "Stack de CloudFormation" ("Creando el stack cunef-etl-crypto-dynamodb ..." con puntos de progreso y "Stack ... creado"; crea la tabla, el rol y la función, tarda 1-2 minutos); "Desplegando Función Lambda" con **"Actualizando función existente..."**, "Código actualizado" y "Configuración actualizada"; y la pregunta `¿Deseas probar la función ahora? (s/n):`. Responde `s`. - Si la prueba funciona, verás un JSON con `statusCode` 200 (con `items_written`, las monedas y la mejor y peor del día), "Items encontrados en DynamoDB: ..." y una tabla con una muestra de los ítems. - El script espera a que la función termine de actualizarse y reintenta solo si AWS la marca como ocupada (`ResourceConflictException`). Si aun así lo ves, espera 10 segundos y repite el comando (se puede repetir). **3. Comprueba el estado cuando quieras:** ```bash ./gestionar-crypto-dynamodb.sh estado ``` **Qué verás**: una línea por elemento con ✓ o ⚠ (stack, función con su estado y tamaño de código, rol, tabla DynamoDB con el número de ítems, reglas de EventBridge, grupo de logs), la fecha de la última ejecución, las últimas líneas del log y una última línea **"Siguiente paso: ..."**. **4. Prueba la función a mano y comprueba el destino:** ```bash aws lambda invoke --function-name crypto-price-tracker-dynamodb \ --cli-binary-format raw-in-base64-out --payload '{}' \ --region eu-north-1 out.json cat out.json aws dynamodb scan --table-name cunef-etl-demo-crypto-prices --select COUNT \ --region eu-north-1 --query Count --output text aws dynamodb query --table-name cunef-etl-demo-crypto-prices \ --key-condition-expression "crypto_name = :cn" \ --expression-attribute-values '{":cn":{"S":"BITCOIN"}}' \ --region eu-north-1 --query "Items[].[timestamp.S,price_usd.N]" --output table ``` (O, más corto: `./gestionar-crypto-dynamodb.sh probar`.) **Qué verás:** `statusCode` 200 en `out.json`; un número (5 tras la primera ejecución, 5 más en cada una siguiente); y una tabla con las marcas de tiempo y el precio en USD de Bitcoin. Por consola: **DynamoDB → Tables → `cunef-etl-demo-crypto-prices` → Explore table items**. **5. (Opcional) Programar la ejecución cada minuto**: `./gestionar-crypto-dynamodb.sh programar` (y `desprogramar` al terminar), o los comandos a mano de la carpeta [Tema 5 · 6 · Programar con EventBridge](../05.06-ProgramarEventBridge.md) con `FN=crypto-price-tracker-dynamodb`. Se apaga sola a las 4 h (ver *Apagado automático*); **bórrala al terminar.** ## Apagado automático La programación de EventBridge **se apaga sola a las 4 horas**: una regla de cada minuto que se olvida sigue ejecutando la función (y llamando a CoinGecko) para siempre. **La regla se desactiva, no se borra**; solo `desprogramar` o `eliminar` la borran. | Quiero... | Comando | |---|---| | Programar la función (4 h por defecto) | `./gestionar-crypto-dynamodb.sh programar` (cada minuto) o, por ejemplo, `./gestionar-crypto-dynamodb.sh programar 5 --horas 2` | | Ver cuánto queda | `./gestionar-crypto-dynamodb.sh estado` (línea `Apagado automático: se apagará a las HH:MM UTC (dentro de N min)`) | | Ampliar el plazo otras 4 h | `./gestionar-crypto-dynamodb.sh extender` (otro plazo: `extender --horas 8`) | | Que no se apague nunca | `./gestionar-crypto-dynamodb.sh programar --permanente` o `extender --permanente` (¡la función seguirá ejecutándose hasta que la quites!) | | Reactivar tras el apagado | `./gestionar-crypto-dynamodb.sh programar` (reactiva la regla y renueva el plazo) | | Quitar la regla / borrarlo todo | `./gestionar-crypto-dynamodb.sh desprogramar` / `./gestionar-crypto-dynamodb.sh eliminar` (funcionan también con la regla desactivada) | Cómo funciona: `programar` guarda el plazo (hora UTC de vencimiento, o `permanente`) en la variable de entorno `APAGAR_TRAS` de la función. En cada ejecución programada, la función la mira y, si ya venció, **desactiva su propia regla** (su rol de IAM solo puede desactivar esa regla) y no ejecuta el ETL. Se nota en la primera ejecución tras el vencimiento (con `rate(1 hour)` puede tardar hasta 1 h). `probar` y las invocaciones a mano no se ven afectadas. Si editas el código con `extraer`, conserva la función `apagado_automatico` y su llamada al principio de `lambda_handler`. ## Errores frecuentes - **`Permission denied` al ejecutar `./gestionar-crypto-dynamodb.sh`.** Solución: `chmod +x *.sh`. Si dice `bad interpreter` o `^M`, el fichero se guardó con saltos de línea de Windows: `tr -d '\r' < gestionar-crypto-dynamodb.sh > x.sh && mv x.sh gestionar-crypto-dynamodb.sh && chmod +x gestionar-crypto-dynamodb.sh`. - **El script se queda mostrando el menú / no hace nada.** Sin argumentos y con terminal muestra el menú: elige una opción o usa un comando (`crear`, `estado`...). Sin terminal imprime la ayuda y sale. - **`Status 429` o `HTTP Error 429` en `error`.** Límite de peticiones de CoinGecko, que es bajo. Espera uno o dos minutos, no encadenes pruebas y vuelve a invocar (`probar`). Con otro código distinto (`403`, `5xx`) la API ha cambiado sus condiciones o está caída: avisa a tu docente. - **`ResourceNotFoundException` al escribir.** La tabla `cunef-etl-demo-crypto-prices` no existe: el stack `cunef-etl-crypto-dynamodb` no se creó o ya se borró. Ejecuta `crear`. - **`ResourceConflictException` al desplegar o al probar.** La función seguía actualizándose. El script ya espera y reintenta; si aun así sale, espera 10 segundos y repite el comando. - **La prueba dice `✗ La función devolvió statusCode 500, no se ha guardado nada`.** Es la forma que tiene el script de avisarte; el motivo va justo debajo (`Motivo: ...`). El más habitual: `429` de CoinGecko. El script continúa y muestra el resumen. - **`crear` dice que el stack está en `ROLLBACK_COMPLETE`, `CREATE_FAILED` u otro estado fallido.** No tienes que hacer nada: el script lo borra y lo vuelve a crear. Si lo prefieres, `eliminar --si` y luego `crear`. - **El script avisa de "una función" o "una tabla fuera de CloudFormation".** Es un resto de la versión antigua del script (que creaba la tabla y la función por su cuenta): los borra (y, con ellos, los ítems que tuvieran) para que el stack pueda crearlos. - **Mi código editado no se despliega / se despliega el original.** `crear` te dice al principio si usa "TU código" o el embebido. Comprueba que el fichero se llama `lambda-crypto-dynamodb.py` o `lambda_function.py` y está en la carpeta desde la que ejecutas el script, o usa `--codigo`. ## Limpieza Hazla cuando termines: ```bash ./gestionar-crypto-dynamodb.sh eliminar ``` Te enseña qué va a borrar y pide confirmación (con `--si` no pregunta). Borra, **forzando** si hace falta y de forma repetible (puedes lanzarlo otra vez sin riesgo): las reglas de EventBridge de la función y su permiso, el stack `cunef-etl-crypto-dynamodb` (la función, el rol y **la tabla DynamoDB con sus datos**; si CloudFormation se atasca, reintenta y retiene solo lo imprescindible), el rol sobrante `LambdaCryptoDynamoDBRole` de versiones antiguas (si existe), cualquier función o tabla suelta con esos nombres y el grupo de logs. **Qué verás**: cinco bloques ("1/5 Programación de EventBridge", "2/5 Datos del ejercicio", "3/5 Stack...", "4/5 Recursos sueltos", "5/5 Comprobación final") y, al final, `✓ Todo eliminado. No queda nada de este ejercicio en AWS.` Si algo no se pudo borrar, lo dice con ⚠ y termina con error: repite el comando. Opcional: ficheros de CloudShell (`cd ~ && rm -rf 05.04-cryptoadynamodb 05.04-cryptoadynamodb.zip`). Esta actividad no usa la infraestructura base, así que **no depende de que la borres**. Si la tienes creada por otras actividades, su borrado (carpeta [05.01](../05.01-infraestructurabase/README.md), sección Limpieza) debe ser el último paso de todo el Tema 5. ## Cómo sabes que has terminado - [ ] `./gestionar-crypto-dynamodb.sh estado` muestra el stack `cunef-etl-crypto-dynamodb` en `CREATE_COMPLETE` y la función con el código real desplegado. - [ ] El `statusCode` de la función es 200 y has comprobado el dato **en el destino**: 5 ítems en la tabla DynamoDB tras la primera ejecución (5 más en cada una siguiente). - [ ] Has hecho la limpieza de esta actividad (`eliminar` terminó con "Todo eliminado") y no queda ningún stack `cunef-etl-crypto-*`. --- Anterior: [Tema 5 · 3 · CoinGecko a S3](../05.03-cryptoas3/README.md) · Siguiente: [Tema 5 · 5 · CoinGecko a S3 y RDS](../05.05-cryptoas3yrds/README.md)