# Tema 5 · 3 · Ejercicio 2, tipo 1: CoinGecko a S3 (con Athena y Glue) Presentación del Tema 5: «Ejercicio 2: CoinGecko (Tipo 1)» (la parte de Glue Visual ETL; las demostraciones de Glue de referencia, en «AWS Glue»). Duración: 25-30 min (sin contar Glue, Athena ni EventBridge). Coste: prácticamente nulo en esta actividad (Lambda, unos pocos ficheros en S3); lo que cuesta es la infraestructura base de la carpeta 05.01. > **Solo haces UNA variante del Ejercicio 2.** Esta carpeta (S3), la 05.04 (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-s3.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-s3.sh crear` etiqueta el stack (Lambda, rol) y la regla de EventBridge con `curso` = `G214`, `universidad` = `CUNEF`, `actividad` = `05.03-cryptoas3` y `script` = `gestionar-crypto-s3.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.03-cryptoas3`. 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 el resultado como ficheros JSON en el bucket S3. Después lo consultas con Athena y, si te lo piden, con un job de Glue Visual ETL. **Qué se crea en AWS:** el stack `cunef-etl-crypto-s3` (rol de IAM + función `crypto-price-tracker`, 30 s, 128 MB). Los datos van al bucket del Paso 0. Lo crea, lo comprueba y lo borra **un único script**: `gestionar-crypto-s3.sh`. **Qué hace la función:** llama a CoinGecko y escribe en el bucket cuatro ficheros JSON: los datos originales (`crypto/raw/history/crypto_prices_AAAAMMDD_HHMMSS.json`), un resumen curado (`crypto/curated/history/crypto_summary_AAAAMMDD_HHMMSS.json`, con total de monedas, capitalización, `top_coins`, mejor y peor moneda de las 24 horas y un `market_sentiment`), y dos copias `crypto/raw/crypto_prices_latest.json` y `crypto/curated/crypto_summary_latest.json` que se sobrescriben en cada ejecución. Cada fichero es **un único objeto JSON en una sola línea** (sin sangrado): así lo lee Athena. ## Lo que debes saber antes de empezar (vale para las tres variantes) 1. **Un solo script hace todo.** `./gestionar-crypto-s3.sh crear` crea el stack de CloudFormation (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 (crear, estado, probar, logs, programar, extraer, eliminar). Con argumentos ejecuta directamente un comando: `crear`, `estado`, `probar`, `logs`, `programar`, `extender`, `desprogramar`, `eliminar`, `extraer` y `ayuda` (lista completa en la sección *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 (por ejemplo, desde otro script) 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 (`LambdaCryptoTrackerRole`), `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), salvo los dos `..._latest.json`. Repetir la invocación es seguro, pero acumula ficheros. 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 (si CoinGecko devuelve las cinco monedas): **4 ficheros JSON bajo `crypto/`** en el bucket; en cada ejecución siguiente, 2 ficheros nuevos (los dos `..._latest.json` se sobrescriben). ## Qué contiene esta carpeta | Fichero | Para qué sirve | En qué paso se usa | |---|---|---| | `gestionar-crypto-s3.sh` | **Todo el ejercicio en un solo archivo:** crea el stack 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 3, 4, 5 y 7, 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-s3.sh extraer ``` Esto escribe `lambda-crypto-s3.py` (el código de la función) y `plantilla-crypto-s3.yaml` (la plantilla de CloudFormation). **No son necesarios para desplegar:** el script ya los lleva. Si editas `lambda-crypto-s3.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 el fichero no compila, no despliega nada y te lo dice). La plantilla extraída solo se usa si se la indicas: `crear --plantilla plantilla-crypto-s3.yaml`. Si el fichero ya existe, `extraer` no lo sobrescribe (para forzarlo: `extraer --forzar`, que guarda una copia `.bak`). ## Antes de empezar - **Depende de [Tema 5 · 1 · Infraestructura base](../05.01-infraestructurabase/README.md)**: usa el bucket del stack `cunef-etl`. Si el stack no existe, el script se detiene con `✗ No se encontró el stack cunef-etl (Paso 0)` **antes de crear nada**: no adopta buckets de las demostraciones ni crea uno propio. - 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.03-cryptoas3`): 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.03-cryptoas3.zip`. 3. Ejecuta: ```bash cd ~ unzip -o 05.03-cryptoas3.zip && cd 05.03-cryptoas3 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.03-cryptoas3` y, si algún fichero da `Permission denied`, ejecuta allí `chmod -R u+rwX . && chmod +x *.sh`. **Qué verás:** `gestionar-crypto-s3.sh` y `README.md`. Ejecuta el script siempre **dentro de esta carpeta**. Las variables de shell se pierden al cerrar la pestaña. ## Los comandos del script `./gestionar-crypto-s3.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, sube el código y, si quieres, prueba la función. Repetible. | | `estado` (`status`, `verificar`) | Muestra qué existe (stack, función, rol, datos en S3, 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 ficheros en S3. | | `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** 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 **0. Recupera las variables del Paso 0** (también si cerraste CloudShell; necesitarás `BUCKET` para Athena y Glue): ```bash OUT() { aws cloudformation describe-stacks --stack-name cunef-etl --region eu-north-1 \ --query "Stacks[0].Outputs[?OutputKey=='$1'].OutputValue" --output text; } BUCKET=$(OUT BucketName); RDS=$(OUT RDSEndpoint); FN=$(OUT LambdaName); ADMINER=$(OUT AdminerURL) echo "Bucket : $BUCKET"; echo "RDS : $RDS"; echo "Lambda : $FN"; echo "Adminer: $ADMINER" ``` **Qué verás:** cuatro líneas con valores (aquí necesitas sobre todo `BUCKET`). Si salen vacías, el stack `cunef-etl` no existe: vuelve a la carpeta 05.01. **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. (Opcional) Mira o edita el código de la función.** Las diapositivas lo llaman "actualiza datos marcados en rojo": el código usa la constante `BUCKET` cuando no le pasan un bucket en el evento (las ejecuciones programadas y las tuyas a mano). **No tienes que escribirla tú:** si el código aún lleva el marcador `cunef-etl-demo-tu-nombre`, el script lo sustituye por el bucket del Paso 0 en el paquete que sube (tu fichero no se modifica). Si quieres verlo o cambiar algo: ```bash ./gestionar-crypto-s3.sh extraer grep -n '^BUCKET' lambda-crypto-s3.py ``` **Qué verás:** `extraer` crea `lambda-crypto-s3.py` y `plantilla-crypto-s3.yaml`; `grep` muestra `14:BUCKET = "cunef-etl-demo-tu-nombre" # <-- Cambia por tu bucket` (si no lo cambias, el script pone tu bucket al desplegar). Si editas el fichero, el siguiente `crear` sube tu versión. **3. Crea todo y despliega el código:** ```bash ./gestionar-crypto-s3.sh crear ``` **Qué verás, en orden** *(los textos pueden variar ligeramente)*: "Verificando prerequisitos" (AWS CLI, credenciales y región); "Configurando Bucket S3" ("Usando bucket del stack ETL: ..."; si no has hecho el Paso 0 el script se para aquí con un mensaje claro y sin crear nada); "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-s3 ..." con puntos de progreso y "Stack ... creado"; 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`. Tarda menos de 3-4 minutos en total. Tras la prueba verás: "Función ejecutada correctamente (statusCode 200)", un JSON con `"statusCode": 200` y dentro de `body` la ruta de los cuatro ficheros creados, "Archivos históricos encontrados: N" (1 en la primera ejecución), las listas "Últimos archivos RAW" y "Archivos LATEST", y el "Resumen del Despliegue". **Si el `statusCode` no es 200**, el script lo marca con `✗ La función devolvió statusCode 500` y escribe el motivo (`error`); sigue después con el resumen. Lee ese mensaje (ver Errores frecuentes). **4. Comprueba el estado cuando quieras:** ```bash ./gestionar-crypto-s3.sh estado ``` **Qué verás**: una línea por elemento con ✓ o ⚠ (infraestructura base, stack, función con su estado y tamaño de código, rol, datos en S3, 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: ..."** que te dice qué hacer. **5. Prueba la función a mano y comprueba el destino:** ```bash aws lambda invoke --function-name crypto-price-tracker \ --cli-binary-format raw-in-base64-out --payload '{}' \ --region eu-north-1 out.json cat out.json aws s3 ls s3://$BUCKET/crypto/ --recursive --human-readable aws s3 cp s3://$BUCKET/crypto/curated/crypto_summary_latest.json - | head -30 ``` (O, más corto: `./gestionar-crypto-s3.sh probar`.) **Qué verás:** `"StatusCode": 200` del CLI; `cat out.json` con `statusCode` 200; la lista con los ficheros de `crypto/raw/` y `crypto/curated/` (más ficheros cada vez que invocas); y el comienzo del resumen JSON (con `timestamp`, `total_coins`, `total_market_cap_usd` y la lista `top_coins`). **6. Athena y Glue (siguiendo las diapositivas del tipo 1).** Ten en cuenta cómo están los ficheros: `crypto/curated/history/` contiene solo los resúmenes con marca de tiempo (cada uno es **un único objeto JSON en una sola línea**, con la lista `top_coins` dentro), y `crypto/raw/` mezcla la carpeta `history/` con el fichero `..._latest.json` (si apuntas una tabla o Glue a `crypto/raw/` leerás cada dato dos veces: usa `crypto/curated/history/` o `crypto/raw/history/`). ### 6a. Athena directamente (no necesita Glue ni ningún rol) Antes de la primera consulta indica el bucket de resultados (*Athena → Settings → Manage*). Después, en el editor de Athena (base `default`), sustituye `TU_BUCKET` por tu bucket: ```sql CREATE EXTERNAL TABLE crypto_summary ( `timestamp` string, total_coins int, total_market_cap_usd double, total_volume_24h_usd double, top_coins array>, best_performer_24h struct, worst_performer_24h struct, market_sentiment string ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' LOCATION 's3://TU_BUCKET/crypto/curated/history/'; -- una fila por moneda y ejecución SELECT s."timestamp", c.name, c.price_eur FROM crypto_summary s CROSS JOIN UNNEST(s.top_coins) AS t(c) ORDER BY 1 DESC, 2 LIMIT 10; SELECT c.name, ROUND(AVG(c.price_eur), 2) AS eur_medio FROM crypto_summary s CROSS JOIN UNNEST(s.top_coins) AS t(c) GROUP BY c.name ORDER BY 2 DESC; ``` **Qué verás:** la primera consulta, 5 filas por cada ejecución de la función (Bitcoin, Ethereum, Solana, Cardano y Polkadot); la segunda, una fila por moneda con su precio medio en euros. **Por qué una línea por fichero:** el SerDe JSON de Athena (`JsonSerDe`) lee **un objeto JSON por línea**. Con un JSON con sangrado (varias líneas por fichero) falla con `HIVE_CURSOR_ERROR: Failed to read file ...`. El código actual de la función ya escribe en una línea. Si ejecutaste la función **con una versión anterior del script** (JSON con sangrado), esos ficheros siguen en `crypto/curated/history/` y harían fallar la consulta: bórralos (`aws s3 rm s3://$BUCKET/crypto/curated/history/ --recursive`; son un dato derivado que se regenera ejecutando la función de nuevo) y repite la invocación. *(Probada en una cuenta real: la sentencia y las dos consultas funcionan tal cual con ficheros de una línea. Si aun así da error de sintaxis en tu versión de Athena, usa el Crawler del Tema 4.)* ### 6b. Glue Visual ETL (si te lo piden) Mismo procedimiento que la demo de la presentación. **Rol de Glue.** Glue necesita un rol. Si no creaste uno en la presentación, crea uno desde CloudShell con permisos solo sobre tu bucket (más ajustado que `AmazonS3FullAccess` de la demo): ```bash GROLE=rol-glue-etl cat > confianza-glue.json <<'EOF' {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"glue.amazonaws.com"},"Action":"sts:AssumeRole"}]} EOF aws iam create-role --role-name $GROLE --assume-role-policy-document file://confianza-glue.json aws iam attach-role-policy --role-name $GROLE \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole cat > politica-glue-s3.json </crypto/curated/history/`. La opción **Multiline** solo hace falta si quedan ficheros antiguos con sangrado. 3. Para tener una fila por moneda, añade la transformación *Explode Array Or Map Into Rows* (menú de transformaciones del editor visual, con el origen como nodo padre): en su pestaña *Transform* elige como columna a explotar `top_coins` y dale un nombre a los elementos, por ejemplo `coin` (cada elemento es un objeto con los datos de una moneda). Después añade *Change Schema* (es el nodo que el editor ya trae entre origen y destino): verás las columnas como `coin.name`, `coin.price_usd`, etc.; mapea cada una a su nombre corto para quedarte con `name`, `price_usd`, `price_eur`, `market_cap_usd`, `volume_24h_usd` y `change_24h_percent`. **En *Change Schema* fija `price_usd` y `price_eur` (y, si las usas, las demás columnas de importes) al tipo `double`:** si no, cuando CoinGecko devuelve un precio entero (Bitcoin: `84847` en vez de `84847.0`) Glue infiere un tipo mixto (`double` e `int`) y `AVG(price_eur)` falla en Athena con `FUNCTION_NOT_FOUND`. El código actual de la función ya escribe siempre los importes con decimales, pero los ficheros de ejecuciones con una versión anterior pueden llevar enteros: bórralos como los de sangrado (`aws s3 rm s3://$BUCKET/crypto/curated/history/ --recursive`) y vuelve a invocar, o fija `double` igualmente. (Alternativa de menor coste: la tabla de Athena de 6a o un *Crawler* sobre esa carpeta, como en el Tema 4.) 4. Destino S3 en formato Parquet con compresión Snappy, **en una carpeta distinta** de la del origen (por ejemplo `s3:///crypto/parquet/`): si escribes junto al origen, Athena intentará leer los JSON como Parquet y fallará. Crea la tabla en el catálogo (base `default`, tabla `crypto_prices`) y en *Job details* baja *Number of workers* a 2. 5. Athena: `SELECT * FROM crypto_prices LIMIT 10;` y `SELECT name, AVG(price_eur) FROM crypto_prices GROUP BY name;`. 6. Al terminar borra el job, las tablas (`crypto_summary`, `crypto_prices`) y el rol de Glue (ver Limpieza); la carpeta `crypto/parquet/` se borra con `eliminar`. *(Probadas con un job equivalente creado por API con ese rol: origen JSON de `crypto/curated/history/`, *Explode* de `top_coins`, *Change Schema* con los seis campos (importes a `double`) y destino Parquet Snappy en `crypto/parquet/` con tabla `crypto_prices` en el catálogo; 5 filas por ejecución, `AVG(price_eur)` correcto en Athena. Funcionan si fijas `price_usd` y `price_eur` a `double`. Los nombres de los nodos de la consola visual no se han comprobado: si alguno se llama distinto, usa la tabla de Athena de 6a.)* **7. (Opcional) Programar la función cada minuto**: `./gestionar-crypto-s3.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`. 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-s3.sh programar` (cada minuto) o, por ejemplo, `./gestionar-crypto-s3.sh programar 5 --horas 2` | | Ver cuánto queda | `./gestionar-crypto-s3.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-s3.sh extender` (otro plazo: `extender --horas 8`) | | Que no se apague nunca | `./gestionar-crypto-s3.sh programar --permanente` o `extender --permanente` (¡la función seguirá ejecutándose hasta que la quites!) | | Reactivar tras el apagado | `./gestionar-crypto-s3.sh programar` (reactiva la regla y renueva el plazo) | | Quitar la regla / borrarlo todo | `./gestionar-crypto-s3.sh desprogramar` / `./gestionar-crypto-s3.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-s3.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-s3.sh > x.sh && mv x.sh gestionar-crypto-s3.sh && chmod +x gestionar-crypto-s3.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. - **El script dice `No se encontró el stack cunef-etl`.** El Paso 0 no existe en esta cuenta: este tipo lo necesita (escribe en su bucket). Créalo en la carpeta 05.01 (`./gestionar-etl-base.sh`) y repite el script. No se ha creado nada. - **`statusCode` 500 con `AccessDenied ... is not authorized to perform: s3:PutObject`.** El rol de la función no deja escribir en tu bucket (stack antiguo creado con una plantilla antigua, `cunef-etl-demo-*`). Solución, sin borrar nada: vuelve a ejecutar `./gestionar-crypto-s3.sh crear`; el script actualiza el stack con la plantilla correcta. Si no se arregla, lee el campo `error` o usa `eliminar` y `crear`. - **`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. - **`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: ...`). Los más habituales: `AccessDenied` (arriba) y `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 fuera de CloudFormation".** Es un resto de la versión antigua del script (que creaba la función por su cuenta): lo borra y deja que el stack la cree. - **Athena: `HIVE_CURSOR_ERROR`, o `FUNCTION_NOT_FOUND` en `AVG(price_eur)` tras Glue.** Ver las explicaciones de los pasos 6a y 6b (ficheros con sangrado y tipos `int`/`double` mezclados). - **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-s3.py` o `lambda_function.py` y está en la carpeta desde la que ejecutas el script, o usa `--codigo`. ## Limpieza Hazla cuando termines, **en este orden**: **1. Todo lo de esta actividad, con un comando:** ```bash ./gestionar-crypto-s3.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, los ficheros `crypto/` que la función guardó en el bucket, el stack `cunef-etl-crypto-s3` (función y rol; si CloudFormation se atasca, reintenta y retiene solo lo imprescindible), el rol sobrante `LambdaCryptoTrackerRole` de versiones antiguas (si existe) y el grupo de logs. **No toca** el stack `cunef-etl` (la infraestructura base) ni otros ejercicios. **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. **2. Lo de Athena y Glue de esta actividad** (solo si lo creaste; el script no lo borra): - **Tablas:** `DROP TABLE crypto_summary;` (y `crypto_prices`) en Athena, o `aws glue delete-table --database-name default --name crypto_summary --region eu-north-1` (y `crypto_prices`). - **Job de Glue:** `aws glue get-jobs --region eu-north-1 --query "Jobs[].Name"` para ver los nombres y `aws glue delete-job --job-name NOMBRE_JOB --region eu-north-1`. Los *crawlers*, con `aws glue delete-crawler --name NOMBRE --region eu-north-1`. - **Rol de Glue** (el de la demostración de Glue o el `rol-glue-etl` del paso 6b): `aws iam list-attached-role-policies --role-name ROL_DE_GLUE` y `aws iam list-role-policies --role-name ROL_DE_GLUE` para ver qué lleva; después `aws iam detach-role-policy --role-name ROL_DE_GLUE --policy-arn arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole` (y lo mismo con `arn:aws:iam::aws:policy/AmazonS3FullAccess` si la consola lo adjuntó), `aws iam delete-role-policy --role-name ROL_DE_GLUE --policy-name s3-lab` (solo si es el de CLI) y `aws iam delete-role --role-name ROL_DE_GLUE`. - **Athena:** si creaste un grupo de trabajo propio, `aws athena delete-work-group --work-group NOMBRE --recursive-delete-option --region eu-north-1`. Los resultados de Athena (por ejemplo `athena-results/`) también hay que borrarlos: `aws s3 rm s3://BUCKET/athena-results/ --recursive`. **3. Opcional:** ficheros de CloudShell (`cd ~ && rm -rf 05.03-cryptoas3 05.03-cryptoas3.zip`). **No borres la infraestructura base todavía** si vas a hacer más actividades. **El borrado de la infraestructura base (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-s3.sh estado` muestra el stack `cunef-etl-crypto-s3` 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**: ficheros en `crypto/raw/` y `crypto/curated/` del bucket. - [ ] (Si te lo pidieron) La tabla de Athena `crypto_summary` devuelve 5 filas por ejecución y/o el job de Glue ha generado `crypto_prices`. - [ ] Has hecho la limpieza de esta actividad (`eliminar` terminó con "Todo eliminado") y no queda ningún stack `cunef-etl-crypto-*`. --- Anterior: [Tema 5 · 2 · ETL Titanic](../05.02-etltitanic/README.md) · Siguiente: [Tema 5 · 4 · CoinGecko a DynamoDB](../05.04-cryptoadynamodb/README.md)