CI (python) 빌드 테스트 액션즈


- ### 개요: MSA 기반으로 각 기능을 수행하는 150 개의 람다를 테스트 * __DB에 저장된 값 까지 참조하여 테스트를 실행하는 코드가 있어서 각 람다마다 독립적인 DynamoDB가 필요했습니다.__ 사용한 도커 이미지 주소: https://hub.docker.com/r/amazon/dynamodb-local 로컬에서 __"무료"__ 로 사용할 수 있는 DynamoDB 입니다.
## 소스코드
test-all-functions.yml ``` name: Test All functions on: pull_request: branches: - staging - main jobs: lambda-function-test: runs-on: ubuntu-latest strategy: matrix: node-version: [14.x] python-version: ["3.7"] steps: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.DEVELOP_AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.DEVELOP_AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: npm install run: | npm install - name: npm test run: | sudo timedatectl set-timezone Asia/Tokyo date python test.py ```
test.py ``` import multiprocessing import os import time LOG_DIR = "log/test" MAX_RETRY_CNT = 5 def test_process(lambda_func, region, failed_list): global LOG_DIR print("================================ {} Start! ===================================".format(lambda_func)) cmd = """cd functions/{lambda_func} ; node ../../node_modules/cross-env/src/bin/cross-env.js \ endpoint=http://localhost:8000 region={region} \ NODE_PATH=../../node_modules:../../layers \ node ../../node_modules/jest/bin/jest.js > ../../{LOG_DIR}/{lambda_func}.log 2>&1 """.format(lambda_func=lambda_func, region=region, LOG_DIR=LOG_DIR) exit_code = os.system(cmd) if exit_code != 0: failed_list.append(lambda_func) cmd = """ grep 'Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout' \ {LOG_DIR}/{lambda_func}.log """.format(lambda_func=lambda_func, region=region, LOG_DIR=LOG_DIR) exit_code = os.system(cmd) if exit_code == 0: retry_test_process(lambda_func, region, failed_list) else: cmd = """ rm {LOG_DIR}/{lambda_func}.log """.format(lambda_func=lambda_func, LOG_DIR=LOG_DIR) os.system(cmd) print("================================ {} Done! ===================================".format(lambda_func)) def retry_test_process (lambda_func, region, failed_list): global LOG_DIR global MAX_RETRY_CNT for i in range(MAX_RETRY_CNT): time.sleep(1) cmd = """cd functions/{lambda_func} ; node ../../node_modules/cross-env/src/bin/cross-env.js \ endpoint=http://localhost:8000 region={region} \ NODE_PATH=../../node_modules:../../layers \ node ../../node_modules/jest/bin/jest.js > ../../{LOG_DIR}/{lambda_func}.log 2>&1 """.format(lambda_func=lambda_func, region=region, LOG_DIR=LOG_DIR) exit_code = os.system(cmd) if exit_code == 0: cmd = """ rm {LOG_DIR}/{lambda_func}.log """.format(lambda_func=lambda_func, LOG_DIR=LOG_DIR) os.system(cmd) failed_list.remove(lambda_func) break else: cmd = """ grep 'Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout' \ {LOG_DIR}/{lambda_func}.log """.format(lambda_func=lambda_func, region=region, LOG_DIR=LOG_DIR) exit_code = os.system(cmd) if exit_code != 0: break if __name__ == '__main__': cmd = """ if [ ! -d {LOG_DIR} ]; then mkdir -p {LOG_DIR}; fi ; if [ \"`docker ps -q -f 'name=dynamodb'`\" != \"\" ]; then docker stop `docker ps -aq -f 'name=dynamodb'`; fi ; if [ \"`docker ps -aq -f 'name=dynamodb'`\" != \"\" ]; then docker rm `docker ps -aq -f 'name=dynamodb'`; fi ; docker run --cpus 2 -m 7000M --name dynamodb -d -p 8000:8000 amazon/dynamodb-local ; """.format(LOG_DIR=LOG_DIR) os.system(cmd) test_list = next(os.walk("./functions"), (None, None, []))[1] failed_list = multiprocessing.Manager().list([]) jobs = [] regions = ["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-east-1", "ap-northeast-3", "ap-northeast-2", "ap-northeast-1", "ap-southeast-1", "ap-southeast-2"] for i in range(len(test_list)): p = multiprocessing.Process(target=test_process, args=(test_list[i], regions[i%len(regions)], failed_list)) jobs.append(p) p.start() if (i+1) == len(test_list) or ((i+1)%len(regions)) == 0: # wait for all processing jobs to finish for job in jobs: job.join() jobs = [] cmd = """ if [ \"`docker ps -q -f 'name=dynamodb'`\" != \"\" ]; then docker stop `docker ps -aq -f 'name=dynamodb'`; fi ; if [ \"`docker ps -aq -f 'name=dynamodb'`\" != \"\" ]; then docker rm `docker ps -aq -f 'name=dynamodb'`; fi """ os.system(cmd) print("Test Done!") error_flag = False for i in range(len(failed_list)): error_flag = True print(failed_list[i]) if error_flag: raise Exception("Error in test processing.") ```

되돌아가기 수정