Skip to main content

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

ConceptoValor
Ramaqa_313
Base de la ramadev_313 (una vez que los stacks estén migrados y validados en DEV)
Cuenta AWSMisma que DEV (endurance / AWS_ACCESS_KEY_ID_DEV)
Regiónus-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)
Pipelinesonarqube → 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 de qa_313 fallará con errores EarlyValidation::ResourceExistenceCheck.


Pre-requisitos

  1. Stacks correctamente migrados y validados en dev_313
  2. Rama qa_313 creada a partir de dev_313
  3. Pipeline qa_313 configurado en bitbucket-pipelines.yml ✅ (ya existe)
  4. Capa ARM64 publicada para QA (ver Paso 1)
  5. 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_313 debe partir de dev_313 para heredar todas las migraciones a Python 3.13 ARM64. NO crear desde QA_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:

  1. Construye la capa ARM64 con Python 3.13
  2. Sube el ZIP a S3
  3. Publica una nueva versión de Lambda Layer
  4. 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
backOfficeQa313backOfficeQaTeamExternalPutQA, TeamExternalOptionsQA + Backoffice-External-QA (ApiKey), Backoffice-External-UsagePlan-QA (UsagePlan)
backOfficecontentQa313backOfficecontentQa8 funciones: AAdNotesxTournamentGetQA, AAdNotesxTournamentSearchGetQA, AAdNotesxTournamentPutQA, y otras
botournamentQa313botournamentQa25 funciones: EditionDeleteQA, EditionGetQA, EditionOptionsQA, y otras
torneosQa313torneosQaStatisticsDetailOptionQA, StatisticsDetailGetQA
flowchannelQa313flowchannelQaFlowChannelPostQA, AddChannelPostQA, AddChannelOptionsQA, SendImagePostQA, SendMessagePostQA
perfilQa313perfilQaTrajectoryGetQA, TournamentHistoryGetQA
colectaQa313colectaQa9 funciones: FundraisingPostQA, FundraisingPutQA, fundraisinggetQA, y otras
dynamoQa313dynamoQa8 funciones: MatchModifyFilterGetQA, MatchInsertGetQA, MatchModifyGetQA, y otras
widgetsQa313widgetsQa8 funciones: RefreshMarketPlaceTriggerQA, RefreshMarketPlaceQA, RefreshMyTeamQA, y otras
comunidadQa313comunidadQa17 funciones: ChannelGetQA, MessagePostQA, ScreenNotificationPostQA, y otras
followersQa313followersQa9 funciones: FollowProfilePostQA, FollowProfileDeleteQA, FollowProfilePutQA, y otras
monitoreoQa313monitoreoQaSin conflictos — puede desplegarse directamente

STACK_MONITOREO es 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:

  1. Obtener el template actual del stack QA_new desde CloudFormation
  2. Eliminar del template los recursos con FunctionName: que conflictúan
  3. Subir el template modificado a S3 y actualizar el stack QA_new
  4. Si el stack QA_new tiene DeletionPolicy: Retain en esas funciones, eliminarlas manualmente de AWS (CloudFormation no las borrará por sí solo)
  5. 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_COMPLETE o UPDATE_COMPLETE antes 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:

  1. sonarqube-step-dynamic — análisis de calidad
  2. quality_gate_dynamic — bloquea si Sonar falla
  3. publish_layer_QA_313 — publica la capa ARM64 si layer_common_313/ tuvo cambios
  4. deploy_QA_313 — invoca pipeline_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 en STACK_*/ para que pipeline_execute.sh los 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:

  1. Llama config_templates_qa_313 → genera {stack}Qa313.yaml con $ENV=QA
  2. Construye el ZIP de la Lambda con SAM
  3. Deploya usando el profile endurance al 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:

OrdenStackStack QA_newConflictos a resolverUn commit por stack
1MONITOREOmonitoreoQaNinguna ✅feat(monitoreo): deploy inicial QA 313
2TORNEOStorneosQa2 FunctionNamesfeat(torneos): deploy inicial QA 313
3PERFILperfilQa2 FunctionNamesfeat(perfil): deploy inicial QA 313
4FLOW_CHANNELflowchannelQa5 FunctionNamesfeat(flow-channel): deploy inicial QA 313
5FOLLOWERSfollowersQa9 FunctionNamesfeat(followers): deploy inicial QA 313
6COLECTAcolectaQa9 FunctionNamesfeat(colecta): deploy inicial QA 313
7DYNAMOdynamoQa8 FunctionNamesfeat(dynamo): deploy inicial QA 313
8WIDGETSwidgetsQa8 FunctionNamesfeat(widgets): deploy inicial QA 313
9BACKOFFICE_CONTENTbackOfficecontentQa8 FunctionNamesfeat(backoffice-content): deploy inicial QA 313
10COMUNIDADcomunidadQa17 FunctionNamesfeat(comunidad): deploy inicial QA 313
11BACKOFFICEbackOfficeQa2 FunctionNames + ApiKey/UsagePlanfeat(backoffice): deploy inicial QA 313
12BACKOFFICE_TOURNAMENTbotournamentQa25 FunctionNamesfeat(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:

RecursoQA_new (Python 3.9)QA_313 (Python 3.13 ARM64)
StackbackOfficeQabackOfficeQa313
Lambda ejemploTeamExternalPutQA (eliminada del stack, en desuso)TeamExternalPutQA (nueva, en stack Qa313)
API GatewayBackofficeBO$ENVBackofficeBOQABackofficeBO313QA
Layercapa 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

  1. No borrar los stacks QA_new hasta validar que QA_313 funciona correctamente en todos los stacks. La coexistencia es el estado transitorio esperado.

  2. Cambios en layer_common_313/: Si se actualiza la capa compartida, hacer un commit con cambios tanto en layer_common_313/ como en al menos un STACK_*/ para que el pipeline publique la nueva capa Y deploye los stacks.

  3. 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.

  4. 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, editar config_templates_qa_313 en config_templates.sh.

  5. 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 en STACK_*/ para que pipeline_execute.sh lo 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ón config_templates_qa_313
  • pipeline_execute.sh — lógica de detección de stacks modificados
  • publish_layer.sh — publicación de capa ARM64