From 600c49d785d447c09c8624c00573146e726477ba Mon Sep 17 00:00:00 2001 From: Luis Helder Date: Thu, 16 Apr 2026 12:21:46 -0300 Subject: [PATCH 1/2] feat: add CloudWatch IAM role resource and shielded deploy target - Add CloudFormation resources to serverless.yml to create the APIGatewayPushToCloudWatchLogs IAM role automatically on deploy, removing the need to pre-create it manually (drops the TODO comment) - Add Makefile target `deploy-lambdas-shielded` for deploying to the shielded stage in eu-central-1 using the testnet-shielded-outputs AWS profile Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/deploy.yml | 32 ++++++++++++++++++++++++++++++++ Makefile | 14 +++++++++++--- pyproject.toml | 3 +++ serverless.yml | 24 ++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2eaeaa0..46eea76 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -256,3 +256,35 @@ jobs: slack_notify: true secrets: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + + deploy-testnet-shielded-outputs: + needs: [init, dependencies] + + if: ${{ needs.init.outputs.environment == 'testnet' }} + + uses: ./.github/workflows/deploy-reusable.yml + with: + aws_region: eu-central-1 + aws_iam_role: arn:aws:iam::264627803661:role/ExplorerServiceGitHubActionsRole + aws_secret_arn: ExplorerService/testnet-shielded-outputs + serverless_stage: shielded + docker_image_tag: testnet-shielded-outputs-${{ github.sha }}-${{ needs.init.outputs.timestamp }} + api_port: 3001 + hathor_core_url: https://node1.shielded-outputs.testnet.hathor.network + hathor_nodes: https://node1.shielded-outputs.testnet.hathor.network + redis_key_prefix: hathor-explorer-service-shielded + redis_port: 6379 + redis_db: 0 + metadata_bucket: hathor-explorer-metadata-testnet-shielded-outputs + cors_allowed_regex: https?://([a-z0-9\-]*\.)*testnet\.hathor\.network + elastic_index: testnet-shielded-outputs-token + elastic_tx_index: testnet-shielded-outputs-tx + elastic_token_balances_index: testnet-shielded-outputs-token_balances + healthcheck_hathor_core_enabled: true + healthcheck_wallet_service_db_enabled: true + healthcheck_elasticsearch_enabled: true + healthcheck_redis_enabled: true + serverless_deploy_prefix: explorer-svc + slack_notify: true + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} diff --git a/Makefile b/Makefile index d0ed4c8..d15d8ca 100644 --- a/Makefile +++ b/Makefile @@ -61,8 +61,12 @@ test: stage=dev # The "AWS_SDK_LOAD_CONFIG=1" is needed to load the AWS credentials from the ~/.aws/config file # This is part of the solution to make it work with `aws sso login` +.PHONY: ensure-poetry-export-plugin +ensure-poetry-export-plugin: + @poetry self show plugins 2>/dev/null | grep -q poetry-plugin-export || poetry self add poetry-plugin-export + .PHONY: deploy-lambdas -deploy-lambdas: +deploy-lambdas: ensure-poetry-export-plugin AWS_SDK_LOAD_CONFIG=1 npx serverless deploy --stage $(stage) --region eu-central-1 .PHONY: deploy-lambdas-ci @@ -70,11 +74,11 @@ deploy-lambdas-ci: npx serverless deploy --stage $(stage) --region eu-central-1 .PHONY: deploy-lambdas-playground -deploy-lambdas-playground: +deploy-lambdas-playground: ensure-poetry-export-plugin AWS_SDK_LOAD_CONFIG=1 npx serverless deploy --stage playground --region eu-central-1 --aws-profile testnet-playground .PHONY: install -install: +install: ensure-poetry-export-plugin npm install poetry install @@ -111,3 +115,7 @@ validate_docs: .PHONY: bundle_docs bundle_docs: npx swagger-cli bundle -o openapi.yml -t yaml text/api-docs.yml + +.PHONY: deploy-lambdas-shielded +deploy-lambdas-shielded: ensure-poetry-export-plugin + AWS_SDK_LOAD_CONFIG=1 npx serverless deploy --stage shielded --region eu-central-1 --aws-profile testnet-shielded-outputs diff --git a/pyproject.toml b/pyproject.toml index 29e6fcd..45d3a3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,9 @@ description = "" authors = ["Hathor Labs "] license = "MIT" +[tool.poetry.requires-plugins] +poetry-plugin-export = ">=1.8" + [tool.poetry.dependencies] python = "^3.9" redis = "^3.5.3" diff --git a/serverless.yml b/serverless.yml index 43a0e3f..680a1e6 100644 --- a/serverless.yml +++ b/serverless.yml @@ -31,8 +31,8 @@ provider: binaryMediaTypes: - 'image/*' logs: - restApi: # TODO: add data to make the serverless create this role by itself - role: arn:aws:iam::${env:AWS_ACCOUNT_ID}:role/APIGatewayPushLogsToCloudWatch + restApi: + role: !GetAtt ApiGatewayCloudWatchLogsRole.Arn vpc: securityGroupIds: - ${env:AWS_VPC_DEFAULT_SG_ID} @@ -1025,3 +1025,23 @@ functions: throttling: maxRequestsPerSecond: 2 maxConcurrentRequests: 2 + +resources: + Resources: + ApiGatewayCloudWatchLogsRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: apigateway.amazonaws.com + Action: sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs + CustomApiGatewayAccountCloudWatchRole: + Type: AWS::ApiGateway::Account + DependsOn: ApiGatewayCloudWatchLogsRole + Properties: + CloudWatchRoleArn: !GetAtt ApiGatewayCloudWatchLogsRole.Arn From c44fd9255e3f1efdf65cd8b74f03626d6c4b89c2 Mon Sep 17 00:00:00 2001 From: Luis Helder Date: Fri, 12 Jun 2026 12:23:53 -0300 Subject: [PATCH 2/2] fix: move API Gateway CloudWatch role to Terraform, use roleManagedExternally AWS::ApiGateway::Account is a singleton per account+region. Managing it in every CloudFormation stack causes stacks to overwrite each other, and deleting any stack resets the account-level logging config globally. Move the IAM role and account association to Terraform (ops-tools), and use roleManagedExternally: true so Serverless never generates its own AWS::ApiGateway::Account resource. Co-Authored-By: Claude Sonnet 4.6 --- serverless.yml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/serverless.yml b/serverless.yml index 680a1e6..2dacdc2 100644 --- a/serverless.yml +++ b/serverless.yml @@ -32,7 +32,7 @@ provider: - 'image/*' logs: restApi: - role: !GetAtt ApiGatewayCloudWatchLogsRole.Arn + roleManagedExternally: true vpc: securityGroupIds: - ${env:AWS_VPC_DEFAULT_SG_ID} @@ -1025,23 +1025,3 @@ functions: throttling: maxRequestsPerSecond: 2 maxConcurrentRequests: 2 - -resources: - Resources: - ApiGatewayCloudWatchLogsRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: - Service: apigateway.amazonaws.com - Action: sts:AssumeRole - ManagedPolicyArns: - - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs - CustomApiGatewayAccountCloudWatchRole: - Type: AWS::ApiGateway::Account - DependsOn: ApiGatewayCloudWatchLogsRole - Properties: - CloudWatchRoleArn: !GetAtt ApiGatewayCloudWatchLogsRole.Arn