Procedimiento de Deploy QA 313 (Python 3.13 ARM64)
Este documento describe el proceso completo para desplegar los stacks migrados a Python 3.13 ARM64 en el entorno QA, usando la rama qa_313.
Contexto
| Concepto | Valor |
|---|---|
| Rama | qa_313 |
| Base de la rama | dev_313 (una vez que los stacks estén migrados y validados en DEV) |
| Cuenta AWS | Misma que DEV (endurance / AWS_ACCESS_KEY_ID_DEV) |
| Región | us-east-1 |
| Capa ARM64 SSM (QA) | /mas10/layers/common/arm64/qa_313 |
| Naming de stacks | {nombre}Qa313 (ej: backOfficeQa313) |
| Naming de templates | {stack}Qa313.yaml (generado al momento del deploy) |
| Pipeline | sonarqube → quality_gate → publish_layer_QA_313 → deploy_QA_313 |
⚠️ IMPORTANTE: DEV y QA usan la misma cuenta AWS. Los stacks QA_new (
backOfficeQa,flowchannelQa, etc.) ya existen y contienen recursos con nombres explícitos (FunctionName, ApiKey, UsagePlan). Si no se resuelven antes del primer deploy, el pipeline deqa_313fallará con erroresEarlyValidation::ResourceExistenceCheck.
Pre-requisitos
- Stacks correctamente migrados y validados en
dev_313 - Rama
qa_313creada a partir dedev_313 - Pipeline
qa_313configurado enbitbucket-pipelines.yml✅ (ya existe) - Capa ARM64 publicada para QA (ver Paso 1)
- Conflictos de nombres resueltos para cada stack (ver Paso 2)
Paso 1 — Crear la rama qa_313
Crear la rama desde el estado estable de dev_313:
git checkout dev_313
git pull origin dev_313
git checkout -b qa_313
git push origin qa_313
La rama
qa_313debe partir dedev_313para heredar todas las migraciones a Python 3.13 ARM64. NO crear desdeQA_new(esa rama usa Python 3.9).
Paso 2 — Publicar la capa ARM64 para QA (primera vez)
La capa ARM64 para QA (/mas10/layers/common/arm64/qa_313) no existe en SSM hasta que se publique por primera vez. El pipeline la publica automáticamente en el step publish_layer_QA_313 cuando hay cambios en layer_common_313/.
Verificar si ya existe
aws ssm get-parameter --name "/mas10/layers/common/arm64/qa_313" --profile local
Si retorna ParameterNotFound, debe publicarse antes del primer deploy.
Publicar manualmente (primera vez o si el pipeline saltó el step)
cd /path/to/mas10_api
AWS_PROFILE_QA=endurance bash publish_layer.sh -q
Esto:
- Construye la capa ARM64 con Python 3.13
- Sube el ZIP a S3
- Publica una nueva versión de Lambda Layer
- Guarda el ARN en SSM en
/mas10/layers/common/arm64/qa_313
Verificar post-publicación
aws ssm get-parameter --name "/mas10/layers/common/arm64/qa_313" --profile local
# Debe retornar: arn:aws:lambda:us-east-1:029548887145:layer:mas10-common-arm64:X
Paso 3 — Resolver conflictos de nombres entre stacks QA_new y QA_313
Este es el paso más crítico. Como DEV y QA comparten la misma cuenta AWS, los stacks del pipeline antiguo (QA_new) ya registran funciones Lambda con nombres como FlowChannelPostQA, TeamExternalPutQA, etc. El nuevo stack flowchannelQa313 intentará crear funciones con esos mismos nombres y CloudFormation rechazará el deploy.
Diagnóstico del problema
El error se verá así en el pipeline:
EarlyValidation::ResourceExistenceCheck: Resource of type
'AWS::Lambda::Function' with identifier 'FlowChannelPostQA' already
exists in stack 'flowchannelQa'.
Stacks con conflictos conocidos
| Stack dev_313 (migrado) | Stack QA_new (activo) | FunctionNames con conflicto |
|---|---|---|
| backOfficeQa313 | backOfficeQa | TeamExternalPutQA, TeamExternalOptionsQA + Backoffice-External-QA (ApiKey), Backoffice-External-UsagePlan-QA (UsagePlan) |
| backOfficecontentQa313 | backOfficecontentQa | 8 funciones: AAdNotesxTournamentGetQA, AAdNotesxTournamentSearchGetQA, AAdNotesxTournamentPutQA, y otras |
| botournamentQa313 | botournamentQa | 25 funciones: EditionDeleteQA, EditionGetQA, EditionOptionsQA, y otras |
| torneosQa313 | torneosQa | StatisticsDetailOptionQA, StatisticsDetailGetQA |
| flowchannelQa313 | flowchannelQa | FlowChannelPostQA, AddChannelPostQA, AddChannelOptionsQA, SendImagePostQA, SendMessagePostQA |
| perfilQa313 | perfilQa | TrajectoryGetQA, TournamentHistoryGetQA |
| colectaQa313 | colectaQa | 9 funciones: FundraisingPostQA, FundraisingPutQA, fundraisinggetQA, y otras |
| dynamoQa313 | dynamoQa | 8 funciones: MatchModifyFilterGetQA, MatchInsertGetQA, MatchModifyGetQA, y otras |
| widgetsQa313 | widgetsQa | 8 funciones: RefreshMarketPlaceTriggerQA, RefreshMarketPlaceQA, RefreshMyTeamQA, y otras |
| comunidadQa313 | comunidadQa | 17 funciones: ChannelGetQA, MessagePostQA, ScreenNotificationPostQA, y otras |
| followersQa313 | followersQa | 9 funciones: FollowProfilePostQA, FollowProfileDeleteQA, FollowProfilePutQA, y otras |
| monitoreoQa313 | monitoreoQa | ✅ Sin conflictos — puede desplegarse directamente |
STACK_MONITOREOes el único stack que puede desplegarse en QA sin pasos previos de resolución de conflictos.
Procedimiento de resolución por stack
El proceso para cada stack con conflictos es idéntico al que se realizó para DEV (ver la sección de resolución de conflictos en MIGRACION_313_STACKS.md):
Para cada stack con conflictos:
- Obtener el template actual del stack QA_new desde CloudFormation
- Eliminar del template los recursos con
FunctionName:que conflictúan - Subir el template modificado a S3 y actualizar el stack QA_new
- Si el stack QA_new tiene
DeletionPolicy: Retainen esas funciones, eliminarlas manualmente de AWS (CloudFormation no las borrará por sí solo) - Desplegar el nuevo stack QA_313
Script Python para limpiar conflictos
El siguiente script es una adaptación genérica del que se usó para DEV. Ajustar la lista FUNCTIONS_TO_REMOVE según la tabla de conflictos de arriba.
#!/usr/bin/env python3
"""
fix_qa_conflicts.py
Remueve FunctionNames conflictivos del template de un stack QA_new
antes de desplegar el equivalente Qa313.
Uso: python3 fix_qa_conflicts.py <nombre_stack_qa>
Ej: python3 fix_qa_conflicts.py flowchannelQa
"""
import boto3
import json
import sys
REGION = "us-east-1"
PROFILE = "local"
# Mapa: nombre_stack_qa -> lista de logical resource IDs a eliminar
RESOURCES_TO_REMOVE = {
"backOfficeQa": [
"TeamExternalPut", "TeamExternalOptions",
"TeamExternalPutPermissionqa", "TeamExternalOptionsPermissionqa",
"BackofficeExternalApiKey", "BackofficeExternalUsagePlan",
"BackofficeExternalUsagePlanKey",
# Eliminar también los outputs si los tiene
],
"flowchannelQa": [
"FlowChannelPost", "AddChannelPost", "AddChannelOptions",
"SendImagePost", "SendMessagePost",
# Agregar aquí sus Permission/* si existen en el template
],
"colectaQa": [
"FundraisingPost", "FundraisingPut", "Fundraisingget",
# Agregar las 6 funciones restantes con sus permissions
],
# ... agregar los demás stacks según tabla de conflictos
}
def fix_stack(stack_name):
session = boto3.Session(profile_name=PROFILE, region_name=REGION)
cf = session.client("cloudformation")
s3 = session.client("s3")
# 1. Obtener template actual
resp = cf.get_template(StackName=stack_name, TemplateStage="Original")
template = json.loads(resp["TemplateBody"])
resources_to_remove = RESOURCES_TO_REMOVE.get(stack_name, [])
if not resources_to_remove:
print(f"No hay recursos configurados para remover de {stack_name}")
return
# 2. Remover recursos conflictivos
removed = []
for r_id in resources_to_remove:
if r_id in template.get("Resources", {}):
del template["Resources"][r_id]
removed.append(r_id)
# También remover outputs que referencien estos recursos
for out_key in list(template.get("Outputs", {}).keys()):
out_val = str(template["Outputs"][out_key])
if any(r in out_val for r in resources_to_remove):
del template["Outputs"][out_key]
print(f"Eliminados de {stack_name}: {removed}")
# 3. Subir template limpio a S3
# Obtener bucket de deploy desde el samconfig del stack
# (Ajustar BUCKET_DEPLOY según corresponda)
BUCKET_DEPLOY = "mas10-deploy-bucket" # <- Obtener del samconfig.toml
s3_key = f"cloudformation-templates/{stack_name}-fixed.json"
s3.put_object(
Bucket=BUCKET_DEPLOY,
Key=s3_key,
Body=json.dumps(template, indent=2)
)
template_url = f"https://{BUCKET_DEPLOY}.s3.amazonaws.com/{s3_key}"
print(f"Template subido a: {template_url}")
# 4. Actualizar el stack
cf.update_stack(
StackName=stack_name,
TemplateURL=template_url,
Capabilities=["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM",
"CAPABILITY_AUTO_EXPAND"],
)
print(f"Stack {stack_name} en actualización...")
waiter = cf.get_waiter("stack_update_complete")
waiter.wait(StackName=stack_name)
print(f"✅ Stack {stack_name} actualizado correctamente")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Uso: python3 fix_qa_conflicts.py <stack_name>")
sys.exit(1)
fix_stack(sys.argv[1])
Eliminar funciones Lambda huérfanas (cuando hay DeletionPolicy: Retain)
Los templates de QA_new tienen DeletionPolicy: Retain. Después de actualizar el stack, las funciones Lambda seguirán existiendo en AWS pero sin dueño. Deben eliminarse manualmente:
# Verificar que la función existe pero no pertenece a ningún stack
aws lambda get-function --function-name FlowChannelPostQA --profile local
# Eliminar la función huérfana
aws lambda delete-function --function-name FlowChannelPostQA --profile local --region us-east-1
# Repetir para cada función conflictiva del stack
Ejemplo completo para STACK_FLOW_CHANNEL
# 1. Verificar el estado actual del stack QA_new
aws cloudformation describe-stacks \
--stack-name flowchannelQa \
--profile local \
--region us-east-1 \
--query "Stacks[0].StackStatus"
# 2. Ejecutar el script de fix (después de completar RESOURCES_TO_REMOVE)
python3 fix_qa_conflicts.py flowchannelQa
# 3. Eliminar funciones huérfanas
for fn in FlowChannelPostQA AddChannelPostQA AddChannelOptionsQA SendImagePostQA SendMessagePostQA; do
echo "Eliminando $fn..."
aws lambda delete-function --function-name $fn --profile local --region us-east-1
done
# 4. Disparar el deploy del nuevo stack (hacer push a qa_313)
git -C /path/to/repo push origin qa_313
Paso 4 — Desplegar en QA 313
⚠️ Los stacks deben desplegarse de a uno por vez, no todos juntos. Esto permite verificar que cada stack quedó en
CREATE_COMPLETEoUPDATE_COMPLETEantes de avanzar al siguiente, y facilita el diagnóstico si algo falla.
Por qué no hacer un deploy masivo
- Si un stack falla, el pipeline aborta y los stacks siguientes NO se despliegan, pero los anteriores ya están deployados → estado inconsistente difícil de rastrear.
- CloudFormation procesa los stacks en paralelo dentro del mismo pipeline run; si dos stacks comparten recursos nombrados (ej: misma Lambda), pueden colisionar.
- Cada stack requiere que los conflictos de su stack QA_new equivalente estén resueltos antes de su deploy. Hacerlos de a uno permite confirmar la resolución de conflictos en el momento correcto.
Ciclo de deploy por stack
Para cada stack, el flujo es:
1. Resolver conflictos del stack QA_new (Paso 3)
2. Hacer el commit con cambios en ESE stack solamente
3. Push → pipeline → esperar a que termine
4. Verificar que el stack quedó en UPDATE_COMPLETE/CREATE_COMPLETE
5. Recién entonces pasar al siguiente stack
Deploy vía pipeline automático (un stack por commit)
El pipeline de qa_313 detecta los stacks modificados comparando git diff --name-only HEAD~1. Para deployar un solo stack, incluir cambios solo en esa carpeta:
# Ejemplo: deployar SOLO STACK_TORNEOS
git add STACK_TORNEOS/
git commit -m "feat(torneos): deploy inicial QA 313"
git push origin qa_313
# Esperar que el pipeline termine antes de continuar con el siguiente stack
El pipeline ejecuta:
sonarqube-step-dynamic— análisis de calidadquality_gate_dynamic— bloquea si Sonar fallapublish_layer_QA_313— publica la capa ARM64 silayer_common_313/tuvo cambiosdeploy_QA_313— invocapipeline_execute.sh qa_313 $BITBUCKET_COMMIT, que deployará solo los stacks incluidos en el diff del commit
IMPORTANTE: Si el commit incluye cambios en múltiples
STACK_*/, el pipeline deployará todos esos stacks en ese run. Para garantizar deploys individuales, un commit = un stack.
Si solo hay cambios en
docs/o en la raíz, el pipeline NO deployará ningún stack. El diff debe incluir cambios enSTACK_*/para quepipeline_execute.shlos detecte.
Deploy manual de un stack específico
cd STACK_{NOMBRE}
bash build_{nombre}.sh 2 qa_313
Esto ejecuta deploy.sh {nombre} {path} qa_313 que:
- Llama
config_templates_qa_313→ genera{stack}Qa313.yamlcon$ENV=QA - Construye el ZIP de la Lambda con SAM
- Deploya usando el profile
enduranceal stack{nombre}Qa313
Deploy del stack de MONITOREO primero (sin conflictos)
STACK_MONITOREO puede desplegarse directamente sin resolver conflictos previos y es el candidato ideal para validar que el entorno QA 313 funciona antes de atacar los stacks con conflictos:
# Hacer un cambio mínimo en STACK_MONITOREO para disparar el pipeline
echo "" >> STACK_MONITOREO/services/grafico/get.py
git add STACK_MONITOREO/
git commit -m "feat(monitoreo): deploy inicial QA 313"
git push origin qa_313
# Verificar resultado antes de continuar
Paso 5 — Orden recomendado de deploy (uno por vez)
Desplegar de a un stack por commit/pipeline run, siguiendo este orden de menor a mayor complejidad de conflictos:
| Orden | Stack | Stack QA_new | Conflictos a resolver | Un commit por stack |
|---|---|---|---|---|
| 1 | MONITOREO | monitoreoQa | Ninguna ✅ | feat(monitoreo): deploy inicial QA 313 |
| 2 | TORNEOS | torneosQa | 2 FunctionNames | feat(torneos): deploy inicial QA 313 |
| 3 | PERFIL | perfilQa | 2 FunctionNames | feat(perfil): deploy inicial QA 313 |
| 4 | FLOW_CHANNEL | flowchannelQa | 5 FunctionNames | feat(flow-channel): deploy inicial QA 313 |
| 5 | FOLLOWERS | followersQa | 9 FunctionNames | feat(followers): deploy inicial QA 313 |
| 6 | COLECTA | colectaQa | 9 FunctionNames | feat(colecta): deploy inicial QA 313 |
| 7 | DYNAMO | dynamoQa | 8 FunctionNames | feat(dynamo): deploy inicial QA 313 |
| 8 | WIDGETS | widgetsQa | 8 FunctionNames | feat(widgets): deploy inicial QA 313 |
| 9 | BACKOFFICE_CONTENT | backOfficecontentQa | 8 FunctionNames | feat(backoffice-content): deploy inicial QA 313 |
| 10 | COMUNIDAD | comunidadQa | 17 FunctionNames | feat(comunidad): deploy inicial QA 313 |
| 11 | BACKOFFICE | backOfficeQa | 2 FunctionNames + ApiKey/UsagePlan | feat(backoffice): deploy inicial QA 313 |
| 12 | BACKOFFICE_TOURNAMENT | botournamentQa | 25 FunctionNames | feat(backoffice-tournament): deploy inicial QA 313 |
Checklist por stack:
[ ] Conflictos del stack QA_new resueltos
[ ] Lambda huérfanas eliminadas de AWS
[ ] Commit con cambios SOLO en STACK_{NOMBRE}/
[ ] Push a qa_313
[ ] Pipeline exitoso (CREATE_COMPLETE / UPDATE_COMPLETE)
[ ] Verificación manual de invocación Lambda
[ ] Avanzar al siguiente stack
Paso 6 — Verificación post-deploy
Verificar stacks activos QA 313
aws cloudformation list-stacks \
--profile local \
--region us-east-1 \
--stack-status-filter UPDATE_COMPLETE CREATE_COMPLETE \
--output json \
| python3 -c "
import json,sys
stacks = json.load(sys.stdin)['StackSummaries']
qa313 = [s['StackName'] for s in stacks if '313' in s['StackName'] and 'Qa' in s['StackName']]
for s in sorted(qa313): print(s)
"
Verificar que la capa QA está referenciada
# Comprobar que el stack usa la capa correcta
aws cloudformation describe-stack-resources \
--stack-name torneosQa313 \
--profile local \
--region us-east-1 \
--query "StackResources[?ResourceType=='AWS::Lambda::LayerVersion']"
Verificar invocación Lambda
aws lambda invoke \
--function-name TorneosGetQA \
--payload '{"warming": true}' \
--profile local \
--region us-east-1 \
/tmp/response.json && cat /tmp/response.json
Coexistencia QA_new y QA_313
Una vez resueltos los conflictos, ambos sistemas coexisten:
| Recurso | QA_new (Python 3.9) | QA_313 (Python 3.13 ARM64) |
|---|---|---|
| Stack | backOfficeQa | backOfficeQa313 |
| Lambda ejemplo | TeamExternalPutQA (eliminada del stack, en desuso) | TeamExternalPutQA (nueva, en stack Qa313) |
| API Gateway | BackofficeBO$ENV → BackofficeBOQA | BackofficeBO313QA |
| Layer | capa Python 3.9 (obsoleta) | arn:..:layer:mas10-common-arm64:X |
Los endpoints de QA_new y QA_313 son diferentes. La migración de tráfico debe coordinarse con el equipo de frontend actualizando las URLs de API Gateway.
Notas y advertencias
-
No borrar los stacks QA_new hasta validar que QA_313 funciona correctamente en todos los stacks. La coexistencia es el estado transitorio esperado.
-
Cambios en
layer_common_313/: Si se actualiza la capa compartida, hacer un commit con cambios tanto enlayer_common_313/como en al menos unSTACK_*/para que el pipeline publique la nueva capa Y deploye los stacks. -
Secretos de QA: Los secrets se obtienen del mismo Secrets Manager que DEV (misma cuenta). Verificar que los valores de QA (
URL,ROLE,USER_POOL_ARN, etc.) en Secrets Manager sean los correctos para QA. -
WARMING_RATE: En QA, la tasa de warming está configurada como
24 hours(igual que DEV para reducir costos). Si se necesita cambiar para QA, editarconfig_templates_qa_313enconfig_templates.sh. -
Trigger del pipeline: Un commit que solo cambie archivos en
docs/o la raíz del repo NO disparará el deploy de stacks. Siempre incluir un cambio real enSTACK_*/para quepipeline_execute.shlo detecte.
Referencias
- MIGRACION_313_STACKS.md — Historial de migraciones y resolución de conflictos
- ARQUITECTURA_DEPLOY_PYTHON313_ARM64.md — Arquitectura general del sistema
config_templates.sh— funciónconfig_templates_qa_313pipeline_execute.sh— lógica de detección de stacks modificadospublish_layer.sh— publicación de capa ARM64