Merge pull request #108 feature-NEWDWH2021-720 into develop-6crm

This commit is contained in:
朝倉 明日香 2022-09-26 11:44:34 +09:00
commit 2db73c55c7
5 changed files with 396 additions and 1 deletions

5
.gitignore vendored
View File

@ -5,8 +5,11 @@ node_modules/
.env
# pythonのキャッシュファイル
__pycache__/
# StepFunctionsステートメント定義変換後のフォルダ
stepfunctions/*/build
**/.vscode/settings.json
# python test
.coverage
.report/
.report/

57
stepfunctions/README.md Normal file
View File

@ -0,0 +1,57 @@
# AWS StepFunctions
## 概要
- MeDaCa基板上でバッチ処理を行うStepFunctionsステートマシン定義を格納する
- ステートマシン定義を格納する際のフォルダ構成は以下とする
- `ステートマシン名/ステートマシン名.json`
- Git管理するファイルは雛形とし、実際の環境にデプロイする定義は`/TOOLS`フォルダ内のツールで生成したものを使用する
- ステートマシン定義の雛形内の環境固有の値は`#{値を表すキー名}`の形式で記述すること
## バージョン情報
- Python3.9.x
## フォルダ構成
```text
.
├── TOOLS -- 環境ごとのステートマシン定義生成ツール置き場
│   ├── convert.conf.yaml -- ステートマシンの変換定義ファイル
│   ├── convert_definition.py -- 定義変換ツールの本体
└── r-crm-datafetch-state -- CRMデータ取得処理のステートマシン定義置き場
├── build -- 【Git管理対象外】TOOLSによって生成され、環境名のフォルダに、環境ごとの値に置き換えたステートマシン定義を配置する
│   ├── product
│   └── staging
└── r-crm-datafetch-state.json -- CRMデータ取得処理のステートマシン定義
```
## ツールの利用方法
### 事前準備
- `/TOOLS`フォルダに移動し、以下のコマンドを実行して、Pythonの仮想環境を作成する
```sh
cd ./TOOLS
# 仮想環境を作成
python -m venv ./.venv
# 仮想環境を有効化
# Mac/Linuxの場合
source ./.venv/bin/activate
# Windowsの場合
.\.venv\Scripts\activate
```
- 以下のコマンドを実行し、YAMLのパッケージをインストールする
```sh
pip install PyYAML
```
### 起動方法
```sh
cd ./TOOLS
python convert_definition.py r-crm-datafetch-state staging
```

View File

@ -0,0 +1,64 @@
# 構成
# config: ルートとなる要素
# <ステートマシン名>: ステートマシン定義名
# <環境名>: stagingかproductのみ
# <ステートマシンの雛形内のプレースホルダー名>:置き換え後の値を設定する
resource:
# 共通定義
common:
# AWSアカウントID
- &AWS_ACCOUNT_ID "826466435614"
# 東京リージョン
- &REGION_AP_NORTHEAST_1 "ap-northeast-1"
# ステージング環境
staging:
# サブネット(PrivateSubnet1)
- &STG_SUBNET_PRI_1A "subnet-0a47b12f6899ab19e"
# サブネット(PrivateSubnet2)
- &STG_SUBNET_PRI_1D "subnet-0ecb92c12eb49ebc3"
# セキュリティグループ(ecs-all)
- &STG_SG_ECS_ALL "sg-051e0fb9925539592"
# セキュリティグループ(ecs-crm-datafetch)
- &STG_SG_CRM_DATAFETCH "sg-0b20b7bb1cb1ab886"
# 本番環境
product:
# サブネット(PrivateSubnet1)
- &PRD_SUBNET_PRI_1A "subnet-0d9bf8cd421cf2489"
# サブネット(PrivateSubnet2)
- &PRD_SUBNET_PRI_1D "subnet-0595f52cf6fd9b9e7"
# セキュリティグループ(ecs-all)
- &PRD_SG_ECS_ALL "sg-05df4823fc789b0fa"
# セキュリティグループ(ecs-crm-datafetch)
# TODO: 本番環境のセキュリティグループを作成したら下記のIDを置き換える
- &PRD_SG_CRM_DATAFETCH "sg-XXXXXXXXXXXXXXXXX"
config:
r-crm-datafetch-state:
# ステージング環境
staging:
# AWSアカウントID
AWS_ACCOUNT_ID: *AWS_ACCOUNT_ID
# 東京リージョン
REGION_AP_NORTHEAST_1: *REGION_AP_NORTHEAST_1
# サブネット(PrivateSubnet1)
SUBNET_PRI_1A: *STG_SUBNET_PRI_1A
# サブネット(PrivateSubnet2)
SUBNET_PRI_1D: *STG_SUBNET_PRI_1D
# セキュリティグループ(ecs-all)
SG_ECS_ALL: *STG_SG_ECS_ALL
# セキュリティグループ(ecs-crm-datafetch)
SG_CRM_DATAFETCH: *STG_SG_CRM_DATAFETCH
# 本番環境
product:
# AWSアカウントID
AWS_ACCOUNT_ID: *AWS_ACCOUNT_ID
# 東京リージョン
REGION_AP_NORTHEAST_1: *REGION_AP_NORTHEAST_1
# サブネット(PrivateSubnet1)
SUBNET_PRI_1A: *PRD_SUBNET_PRI_1A
# サブネット(PrivateSubnet2)
SUBNET_PRI_1D: *PRD_SUBNET_PRI_1D
# セキュリティグループ(ecs-all)
SG_ECS_ALL: *PRD_SG_ECS_ALL
# セキュリティグループ(ecs-crm-datafetch)
SG_CRM_DATAFETCH: *PRD_SG_CRM_DATAFETCH

View File

@ -0,0 +1,142 @@
import os
import sys
from string import Template
# you need to install module `PyYAML`
import yaml
CONF_FOLDER_PATH = '.'
DEFINITION_FOLDER_BASE = '..'
CONVERTED_FOLDER_BASE = 'build'
CONVERT_CONF = 'convert_config.yaml'
CHAR_CODE = 'utf-8'
PRD_NAME = 'product'
STG_NAME = 'staging'
class StateMachineTemplate(Template):
"""StepFunctionsステートマシンの置き換えるためのテンプレート
生のTemplateクラスはプレースホルダーを表す文字列が`$`ステートマシン定義内で常用する文字と被ってしまうためサブクラス化
"""
# `#{プレースホルダー}`という形式が使えるようになる
delimiter = "#"
def __init__(self, template: str) -> None:
super().__init__(template)
def main(args=None):
"""
args1: StepFunctionsステートマシン名
args2: 変換先環境名product or staging
"""
# カレントディレクトリ移動
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# 引数チェック
state_name, env_name = check_args(args)
print('引数確認OK')
# ファイル存在チェック
check_file_exist(state_name)
print('ファイル存在確認OK')
# フォルダがなければ作る
env_name_folder = f'{DEFINITION_FOLDER_BASE}/{state_name}/{CONVERTED_FOLDER_BASE}/{env_name}'
if not os.path.isdir(env_name_folder):
os.makedirs(env_name_folder, exist_ok=True)
print(f'定義生成用フォルダを作成しました。フォルダ名:{env_name_folder}')
# 変換
converted_file_name = convert_definition(state_name, env_name)
print(f'変換が完了しました ファイル名:{converted_file_name}')
return
def check_args(args):
try:
check_length(args)
check_aws_environment(args)
return args[1], args[2]
except Exception as e:
raise Exception(f'引数確認に失敗しました {e}')
def check_length(args):
if len(args) != 3:
raise Exception('引数の数が異常です')
return
def check_aws_environment(args):
if args[2] not in [STG_NAME, PRD_NAME]:
raise Exception('第2引数が不正です')
return
def check_file_exist(state_name):
if not os.path.isfile(f'{CONF_FOLDER_PATH}/{CONVERT_CONF}'):
raise Exception('変換定義ファイルが存在しません')
if not os.path.isfile(f'{DEFINITION_FOLDER_BASE}/{state_name}/{state_name}.json'):
raise Exception('変換元のステートマシン定義が存在しません')
return
def convert_definition(state_name, env_name):
try:
# 定義フォルダのパス生成
from_folder = f'{DEFINITION_FOLDER_BASE}/{state_name}'
to_folder = f'{from_folder}/{CONVERTED_FOLDER_BASE}/{env_name}'
# 変換定義の読み込み
convert_config = read_env_specific_config(state_name, env_name)
print(convert_config)
# テンプレートとなるファイルを読み込み
with open(f'{from_folder}/{state_name}.json', mode='r', encoding=CHAR_CODE) as from_file:
state_json = from_file.read()
state_json_template = StateMachineTemplate(state_json)
# 環境固有の値を置き換え
substituted_state_json = state_json_template.substitute(ENV_NAME=env_name, **convert_config)
# 書き込みファイルオープン
with open(f'{to_folder}/{state_name}.json', mode='w',
encoding=CHAR_CODE, newline='\n') as to_file:
to_file.write(substituted_state_json)
return f'{to_folder}/{state_name}.json'
except Exception as e:
raise Exception(f'変換に失敗しました {e}')
def read_env_specific_config(state_name, env_name):
try:
# 変換定義ファイルオープン
with open(f'{CONF_FOLDER_PATH}/{CONVERT_CONF}', mode='r', encoding=CHAR_CODE) as convert_conf_file:
config_yaml = yaml.load(convert_conf_file, yaml.Loader)
# 変換定義からステートマシンの環境固有の値を取得
env_specific_config = config_yaml['config'][state_name][env_name]
return env_specific_config
except Exception as e:
Exception('変換定義ファイルの読み込みに失敗しました')
if __name__ == '__main__':
main(args=sys.argv)

View File

@ -0,0 +1,129 @@
{
"Comment": "crm-datafetch root job",
"StartAt": "params",
"States": {
"params": {
"Comment": "パラメータ設定",
"Type": "Pass",
"Parameters": {
"sns": {
"TopicArn": "arn:aws:sns:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:nds-notice-#{ENV_NAME}"
},
"ecs": {
"Cluster": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:cluster/mbj-newdwh2021-#{ENV_NAME}-crm-ecs",
"LaunchType": "FARGATE",
"NetworkConfiguration": {
"AwsvpcConfiguration": {
"Subnets": [
"#{SUBNET_PRI_1A}",
"#{SUBNET_PRI_1D}"
],
"SecurityGroups": [
"#{SG_ECS_ALL}",
"#{SG_CRM_DATAFETCH}"
],
"AssignPublicIp": "DISABLED"
}
}
}
},
"ResultPath": "$.params",
"Next": "crm-datafetch-diff"
},
"crm-datafetch-diff": {
"Comment": "CRMデータ取得",
"Type": "Task",
"Resource": "arn:aws:states:::ecs:runTask.sync",
"Parameters": {
"Cluster.$": "$.params.ecs.Cluster",
"LaunchType.$": "$.params.ecs.LaunchType",
"TaskDefinition": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:task-definition/mbj-newdwh2021-#{ENV_NAME}-task-crm-datafetch",
"NetworkConfiguration.$": "$.params.ecs.NetworkConfiguration"
},
"Retry": [
{
"ErrorEquals": ["States.ALL"],
"BackoffRate": 2,
"IntervalSeconds": 3,
"MaxAttempts": 3
}
],
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"ResultPath": "$.result",
"Next": "ErrorEnd"
}
],
"ResultPath": "$.result",
"Next": "Wait"
},
"Wait": {
"Type": "Wait",
"Seconds": 5,
"Next": "crm-datafetch-all"
},
"crm-datafetch-all": {
"Comment": "CRMデータ全量取得",
"Type": "Task",
"Resource": "arn:aws:states:::ecs:runTask.sync",
"Parameters": {
"Cluster.$": "$.params.ecs.Cluster",
"LaunchType.$": "$.params.ecs.LaunchType",
"TaskDefinition": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:task-definition/mbj-newdwh2021-#{ENV_NAME}-task-crm-datafetch",
"NetworkConfiguration.$": "$.params.ecs.NetworkConfiguration",
"Overrides": {
"ContainerOverrides": [
{
"Name": "mbj-newdwh2021-#{ENV_NAME}-container-crm-datafetch",
"Environment": [
{
"Name": "OBJECT_INFO_FILENAME",
"Value": "crm_object_list_all.json"
}
]
}
]
}
},
"Retry": [
{
"ErrorEquals": ["States.ALL"],
"BackoffRate": 2,
"IntervalSeconds": 3,
"MaxAttempts": 3
}
],
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"ResultPath": "$.result",
"Next": "ErrorEnd"
}
],
"ResultPath": "$.result",
"Next": "SuccessNotice"
},
"SuccessNotice": {
"Comment": "正常終了通知",
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn.$": "$.params.sns.TopicArn",
"Subject": "StepFunctions正常終了通知",
"Message.$": "States.Format('CRMデータ取得処理が完了しました。\n\n 対象のステートマシン:{} \n 実行ID{}', $$.StateMachine.Id, $$.Execution.Name)"
},
"Next": "NormalEnd"
},
"NormalEnd": {
"Comment": "正常終了",
"Type": "Succeed"
},
"ErrorEnd": {
"Comment": "異常終了",
"Type": "Fail",
"Error": "StatesError",
"Cause": "StepFunctions ErrorEnd"
}
}
}