이 글은 Apex(Terraform)로 API Gateway 구성하기 #1에서 이어진 글이다.
API Gateway 로깅
API Gateway가 안되는 이유를 보려면 API Gateway에서 발생한 오류를 로그로 남겨서 봐야 한다. API Gateway에는 오류를 보는 기능이 없으므로 확인하는 CloudWatch에 오류를 남길 수 있도록 설정해야 한다.
API Gateway에서는 리전 내 전체 설정에서 CloudWatch에 로그를 남길 수 있는 권한이 있는 role의 ARN을 설정해 놓고 각 API의 리소스나 메서드에서 이 role을 사용해서 로깅을 키거나 끌 수 있다. 이 설정은 특정 Lambda에 종속 된 것이 아니므로 별도의 파일 cloudwatch.tf
로 분리했다.
// infrastructure/cloudwatch.tf
// cloudwatch를 사용할 policy
data "aws_iam_policy_document" "allow_log_to_cloudwatch" {
statement {
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:PutLogEvents",
"logs:GetLogEvents",
"logs:FilterLogEvents"
]
resources = [
"*",
]
}
}
resource "aws_iam_role_policy" "allow_log_to_cloudwatch" {
name = "allow_log_to_cloudwatch"
role = "${aws_iam_role.api_gateway_cloudwatch.id}"
policy = "${data.aws_iam_policy_document.allow_log_to_cloudwatch.json}"
}
// cloudwatch를 사용할 role
data "aws_iam_policy_document" "allow_log_to_cloudwatch_from_apigateway" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
}
}
resource "aws_iam_role" "api_gateway_cloudwatch" {
name = "api_gateway_cloudwatch"
assume_role_policy = "${data.aws_iam_policy_document.allow_log_to_cloudwatch_from_apigateway.json}"
}
// 로깅을 위한 CloudWatch 설정
resource "aws_api_gateway_account" "api_gateway_account" {
cloudwatch_role_arn = "${aws_iam_role.api_gateway_cloudwatch.arn}"
}
여기서는 IAM에서 policy를 하나 만들고 role을 하나 만들어서 policy에 연결한 후에 API Gateway에 설정한 것이다. aws_iam_policy_document.allow_log_to_cloudwatch
에서 정책 규칙을 정의해서 aws_iam_role_policy
로 새로운 policy를 만들고 aws_iam_policy_document.allow_log_to_cloudwatch_from_apigateway
에서 role의 규칙을 만든 후 aws_iam_role
에서 role을 만들고 이 role을 policy와 연결했다. 그리고 이렇게 생성한 role의 ARN은 aws_api_gateway_account
에서 설정했다.
이를 적용해보자.
$ apex infra plan
+ aws_api_gateway_account.api_gateway_account
cloudwatch_role_arn: "${aws_iam_role.api_gateway_cloudwatch.arn}"
throttle_settings.#: "<computed>"
-/+ aws_api_gateway_deployment.status_api_test
created_date: "2017-05-07T17:22:22Z" => "<computed>"
description: "Deployed at 2017-05-07T17:22:22Z" => "Deployed at 2017-05-07T17:36:36Z"
execution_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test" => "<computed>"
invoke_url: "https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test" => "<computed>"
rest_api_id: "1w03z7dzmi" => "1w03z7dzmi"
stage_description: "2017-05-07T17:22:22Z" => "2017-05-07T17:36:36Z" (forces new resource)
stage_name: "test" => "test"
+ aws_iam_role.api_gateway_cloudwatch
arn: "<computed>"
assume_role_policy: "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"\",\n \"Effect\": \"Allow\",\n \"Action\": \"sts:AssumeRole\",\n \"Principal\": {\n \"Service\": \"apigateway.amazonaws.com\"\n }\n }\n ]\n}"
create_date: "<computed>"
name: "api_gateway_cloudwatch"
path: "/"
unique_id: "<computed>"
+ aws_iam_role_policy.allow_log_to_cloudwatch
name: "allow_log_to_cloudwatch"
policy: "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Sid\": \"\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"logs:PutLogEvents\",\n \"logs:GetLogEvents\",\n \"logs:FilterLogEvents\",\n \"logs:DescribeLogStreams\",\n \"logs:DescribeLogGroups\",\n \"logs:CreateLogStream\",\n \"logs:CreateLogGroup\"\n ],\n \"Resource\": \"*\"\n }\n ]\n}"
role: "${aws_iam_role.api_gateway_cloudwatch.id}"
-/+ aws_lambda_permission.status_api_resource_check_post
action: "lambda:InvokeFunction" => "lambda:InvokeFunction"
function_name: "github-status_github-status" => "github-status_github-status"
principal: "apigateway.amazonaws.com" => "apigateway.amazonaws.com"
source_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test/POST/check" => "${aws_api_gateway_deployment.status_api_test.execution_arn}/${aws_api_gateway_integration.status_api_resource_check_post.integration_http_method}${aws_api_gateway_resource.status_api_resource_check.path}" (forces new resource)
statement_id: "AllowInvokeFromAPIGateway" => "AllowInvokeFromAPIGateway"
Plan: 5 to add, 0 to change, 2 to destroy.
API Gateway의 설정 화면에 들어가면 ARN이 잘 설정된 것을 볼 수 있다.
이제 API에 로깅을 활성화 해야 한다.
// infrastructure/github-status.tf
// API 메서드 설정
resource "aws_api_gateway_method_settings" "status_api_resource_check_post" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
stage_name = "${aws_api_gateway_deployment.status_api_test.stage_name}"
method_path = "${aws_api_gateway_resource.status_api_resource_check.path_part}/${aws_api_gateway_method.status_api_resource_check_post.http_method}"
settings {
metrics_enabled = true
logging_level = "INFO"
}
}
이는 aws_api_gateway_method_settings
를 사용하는데 메서드 레벨에서 로깅 등을 활성화할 수 있다. 웹 콘솔에서는 스테이지 단위에서 설정을 한 후 다른 리소스나 메서드에서는 이 설정을 상속받는데 Terraform에서는 그런 방법을 못 찾았고 위처럼 스테이지와 메서드를 지정하고 로깅 등을 설정할 수 있다.
$ apex infra plan
-/+ aws_api_gateway_deployment.status_api_test
created_date: "2017-05-07T17:42:26Z" => "<computed>"
description: "Deployed at 2017-05-07T17:42:25Z" => "Deployed at 2017-05-07T17:46:07Z"
execution_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test" => "<computed>"
invoke_url: "https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test" => "<computed>"
rest_api_id: "1w03z7dzmi" => "1w03z7dzmi"
stage_description: "2017-05-07T17:42:25Z" => "2017-05-07T17:46:07Z" (forces new resource)
stage_name: "test" => "test"
+ aws_api_gateway_method_settings.status_api_resource_check_post
method_path: "check/POST"
rest_api_id: "1w03z7dzmi"
settings.#: "1"
settings.0.logging_level: "INFO"
settings.0.metrics_enabled: "true"
stage_name: "test"
-/+ aws_lambda_permission.status_api_resource_check_post
action: "lambda:InvokeFunction" => "lambda:InvokeFunction"
function_name: "github-status_github-status" => "github-status_github-status"
principal: "apigateway.amazonaws.com" => "apigateway.amazonaws.com"
source_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test/POST/check" => "${aws_api_gateway_deployment.status_api_test.execution_arn}/${aws_api_gateway_integration.status_api_resource_check_post.integration_http_method}${aws_api_gateway_resource.status_api_resource_check.path}" (forces new resource)
statement_id: "AllowInvokeFromAPIGateway" => "AllowInvokeFromAPIGateway"
Plan: 3 to add, 0 to change, 2 to destroy.
이를 적용하고 CloudWatch 로그에 가면 다음과 같은 로그를 볼 수 있다.
17:50:30 Verifying Usage Plan for request: a9951f1b-334d-11e7-8a79-7fd16e045aa1. API Key: API Stage: 1w03z7dzmi/test
17:50:30 API Key authorized because method 'POST /check' does not require API Key. Request will not contribute to throttle or quota limits
17:50:30 Usage Plan check succeeded for API Key and API Stage 1w03z7dzmi/test
17:50:30 Starting execution for request: a9951f1b-334d-11e7-8a79-7fd16e045aa1
17:50:30 HTTP Method: POST, Resource Path: /check
17:50:31 Execution failed due to configuration error: No match for output mapping and no default output mapping configured
17:50:31 Method completed with status: 500
위에서 Internal server error
오류가 발생한 이유가 기본 output mapping이 설정되지 않았기 때문임을 알 수 있다. Lambda 자체의 오류가 아니라면 API Gateway에서 제공하는 테스트 기능으로 확인하거나 이 로그를 키고 확인해야 한다.
출력 매핑 추가
여기서 출력 매핑이란 것은 HTTP API에 응답을 결정하는 역할을 하로 아래 API 화면에서 Method Response와 Integration Response를 의미한다.
HTTP로 200을 줄 수도 있고 201이나 400, 404등 다양한 응답을 줄 수 있으므로 매핑 조건에 따라 어떤 응답을 줄지를 결정할 수 있다. 좀 더 설명하면 Lambda의 결과를 받아서 상태 코드와 함께 응답 데이터 형식을 결정해서 반환할 수 있다.
여기서는 일단 기본 응답인 200 OK
를 추가해 보자.
// infrastructure/github-status.tf
// 200 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_200" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_200" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_200.status_code}"
response_templates = {
"application/json" = ""
}
}
일단 aws_api_gateway_method_response
로 메서드의 응답을 하나 정의해야 한다. 여기서는 상태 코드를 200으로 정의했고 응답의 모델은 기본으로 만들어지는 Empty
모델을 application/json
으로 응답하도록 정의했다. aws_api_gateway_integration_response
에서는 Lambda와 응답을 통합하는 역할을 하는데 여기서 application/json
형식으로 응답하도록 정의했고 그 내용에는 아무것도 넣지 않았다.
$ apex infra plan
-/+ aws_api_gateway_deployment.status_api_test
created_date: "2017-05-07T17:59:46Z" => "<computed>"
description: "Deployed at 2017-05-07T17:59:45Z" => "Deployed at 2017-05-07T18:00:01Z"
execution_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test" => "<computed>"
invoke_url: "https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test" => "<computed>"
rest_api_id: "1w03z7dzmi" => "1w03z7dzmi"
stage_description: "2017-05-07T17:59:45Z" => "2017-05-07T18:00:01Z" (forces new resource)
stage_name: "test" => "test"
+ aws_api_gateway_integration_response.status_api_resource_check_post_200
http_method: "POST"
resource_id: "txi2jt"
response_templates.%: "1"
response_templates.application/json: ""
rest_api_id: "1w03z7dzmi"
status_code: "200"
+ aws_api_gateway_method_response.status_api_resource_check_post_200
http_method: "POST"
resource_id: "txi2jt"
response_models.%: "1"
response_models.application/json: "Empty"
rest_api_id: "1w03z7dzmi"
status_code: "200"
Plan: 3 to add, 0 to change, 1 to destroy.
이를 적용한 뒤에 API를 다시 테스트하면 Lambda가 반환하는 결과가 200 OK로 반환된 것을 확인할 수 있다.
$ curl -i \
-X POST https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test/check \
-d '{ "url": "https://status.github.com/api/last-message.json"}'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 107
Connection: keep-alive
Date: Sun, 07 May 2017 18:04:37 GMT
x-amzn-RequestId: a1f41398-334f-11e7-b887-d166a220b6ab
X-Amzn-Trace-Id: sampled=0;root=1-590f61b5-8338679ddbc8cf6feeadade5
X-Cache: Miss from cloudfront
Via: 1.1 5d1ba4039f11e793a35923f543e4f02a.cloudfront.net (CloudFront)
X-Amz-Cf-Id: Q4kIiAq7w8GK3SVVm3fmSmf3qzNlSZ-bsBElSl8rYzT7mGJi-HZXaA==
"{\"status\":\"good\",\"body\":\"Everything operating normally.\",\"created_on\":\"2017-05-06T19:04:12Z\"}"
이제 정상적인 동작은 마무리되었다. 이 API에 인증은 추가하지 않았지만, 이는 여기서는 다루지 않는다.
예외 응답 추가
200 OK는 잘 반환하게 되었는데 HTTP API라면 요청 형식이 잘못되거나 Lambda에서 오류가 발생한 경우 4xx, 5xx 응답을 줄 수 있어야 한다. 이를 위해서 200 응답처럼 응답 매핑을 추가해야 한다.
// infrastructure/github-status.tf
// 400 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_400" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "400"
response_models = {
"application/json" = "Error"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_400" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_400.status_code}"
selection_pattern = ".*BadRequest.*"
response_templates = {
"application/json" = <<EOF
#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))
{
"message": "$errorMessageObj.message",
"stack": "$errorMessageObj.stackTrace",
"requestId": "$errorMessageObj.requestId"
}
EOF
}
}
// 500 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_500" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "500"
response_models = {
"application/json" = "Error"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_500" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_500.status_code}"
selection_pattern = ".*InternalServerError.*"
response_templates = {
"application/json" = <<EOF
#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))
{
"message": "$errorMessageObj.message",
"stack": "$errorMessageObj.stackTrace",
"requestId": "$errorMessageObj.requestId"
}
EOF
}
}
앞에 200과 같이 400, 500 용으로 aws_api_gateway_method_response
와 aws_api_gateway_integration_response
를 선언했다. 200과 다른 부분은 selection_pattern = ".*\BadRequest\]*"
부분인데 이는 Lambda에서 넘어온 응답의 패턴을 정규식으로 비교해서 매치되는 경우 기본 응답 대신 이 응답을 사용한다. 앞에 Lambda에서 요청 오류이면 [BadRequest]
를, 다른 오류가 생기면 [InternalServerError]
오류 메시지에 추가한 것을 기억하는가? 여기서 이 메시지를 비교한 것이다. 앞에서도 설명했듯이 오류 객체 JSON을 문자열로 반환했으므로 전체가 문자열로 받게 되는데 여기서 정규식으로 원하는 부분을 비교해서 응답과 매칭시키면 된다.
response_templates
에서는 application/json
로 반환하는데 받은 객체를 그대로 주는 대신 응답 데이터를 구성했다. API Gateway에서 제공하는 유틸리티 함수가 있는데 $util.parseJson($input.path('$.errorMessage'))
에서 Lambda에서 받은 $.errorMessage
를 JSON으로 변환해서 $errorMessageObj
로 할당했다. 그리고 이 객체를 이용해서 원하는 JSON을 새로 구성해서 응답하도록 설정했다.
$ apex infra plan
-/+ aws_api_gateway_deployment.status_api_test
created_date: "2017-05-07T18:00:26Z" => "<computed>"
description: "Deployed at 2017-05-07T18:00:25Z" => "Deployed at 2017-05-07T18:16:54Z"
execution_arn: "arn:aws:execute-api:ap-northeast-1:410000000000:1w03z7dzmi/test" => "<computed>"
invoke_url: "https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test" => "<computed>"
rest_api_id: "1w03z7dzmi" => "1w03z7dzmi"
stage_description: "2017-05-07T18:00:25Z" => "2017-05-07T18:16:54Z" (forces new resource)
stage_name: "test" => "test"
+ aws_api_gateway_integration_response.status_api_resource_check_post_400
http_method: "POST"
resource_id: "txi2jt"
response_templates.%: "1"
response_templates.application/json: "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n\n{\n \"message\": \"$errorMessageObj.message\",\n \"stack\": \"$errorMessageObj.stackTrace\",\n \"requestId\": \"$errorMessageObj.requestId\"\n}\n"
rest_api_id: "1w03z7dzmi"
selection_pattern: ".*BadRequest.*"
status_code: "400"
+ aws_api_gateway_integration_response.status_api_resource_check_post_500
http_method: "POST"
resource_id: "txi2jt"
response_templates.%: "1"
response_templates.application/json: "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n\n{\n \"message\": \"$errorMessageObj.message\",\n \"stack\": \"$errorMessageObj.stackTrace\",\n \"requestId\": \"$errorMessageObj.requestId\"\n}\n"
rest_api_id: "1w03z7dzmi"
selection_pattern: ".*InternalServerError.*"
status_code: "500"
+ aws_api_gateway_method_response.status_api_resource_check_post_400
http_method: "POST"
resource_id: "txi2jt"
response_models.%: "1"
response_models.application/json: "Error"
rest_api_id: "1w03z7dzmi"
status_code: "400"
+ aws_api_gateway_method_response.status_api_resource_check_post_500
http_method: "POST"
resource_id: "txi2jt"
response_models.%: "1"
response_models.application/json: "Error"
rest_api_id: "1w03z7dzmi"
status_code: "500"
+ aws_api_gateway_method_settings.status_api_resource_check_post
method_path: "check/POST"
rest_api_id: "1w03z7dzmi"
settings.#: "1"
settings.0.logging_level: "INFO"
settings.0.metrics_enabled: "true"
stage_name: "test"
Plan: 6 to add, 0 to change, 1 to destroy.
이를 적용하고 실제로 각 오류를 테스트해보자.
$ curl -i \
-X POST https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test/check \
-d '{ }'
HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 234
Connection: keep-alive
Date: Sun, 07 May 2017 18:17:57 GMT
x-amzn-RequestId: 7ef3703e-3351-11e7-9007-39d9f5af8bce
X-Amzn-Trace-Id: sampled=0;root=1-590f64d5-c36878c3f02a3717ec66c3d3
X-Cache: Error from cloudfront
Via: 1.1 e16834aaac4fd814ae3edf9e633f4567.cloudfront.net (CloudFront)
X-Amz-Cf-Id: GrkJP5YTUc1LfwQO6Sc4cimOzKVFFAXDfj3_chP7_PHCpJDjYyQBmw==
{
"message": "[BadRequest] URL required",
"stack": "Error: [BadRequest] URL required
at ping (/var/task/index.js:6:25)
at exports.handle (/var/task/index.js:18:3)",
"requestId": "7ef40ba9-3351-11e7-b596-5dd2f1ad2723"
}
요청에 url
속성을 전달하지 않으면 400 Bad Request가 반환된다.
$ curl -i \
-X POST https://1w03z7dzmi.execute-api.ap-northeast-1.amazonaws.com/test/check \
-d '{ "url": "noturl" }'
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 448
Connection: keep-alive
Date: Sun, 07 May 2017 18:18:12 GMT
x-amzn-RequestId: 87efd9cd-3351-11e7-8a79-7fd16e045aa1
X-Amzn-Trace-Id: sampled=0;root=1-590f64e4-033e3c311bef8a2e208d0a5f
X-Cache: Error from cloudfront
Via: 1.1 11a0261328fa2fb9ebe64a59fd132104.cloudfront.net (CloudFront)
X-Amz-Cf-Id: rt2k9xQFGIxKQB1z4wuapWD5gasgBsR05V9APGlTLEyRGHlTs5Nh_g==
{
"message": "[InternalServerError] Invalid URI "noturl"",
"stack": "Error: Invalid URI "noturl"
at Request.init (/var/task/node_modules/request/request.js:276:31)
at new Request (/var/task/node_modules/request/request.js:130:8)
at request (/var/task/node_modules/request/index.js:54:10)
at ping (/var/task/index.js:8:3)
at exports.handle (/var/task/index.js:18:3)",
"requestId": "87ef8bdc-3351-11e7-82ab-d92b9a17bfda"
}
그리고 다른 오류가 발생하도록 url이 아닌 다른 문자열을 전달하자 500 오류가 정상적으로 반환되었다.
마무리
물론 API Gateway가 많은 기능을 제공하므로 제대로 사용하려면 이외에도 많은 설정이 필요하지만, 이 기본 골격과 각 리소스에 대한 이해를 하기 위해서 꽤 많은 시간을 소비했다. 이 구성이 가장 좋은 구성이라는 것은 아니지만, 이 기본 구조를 찬찬히 살펴보면 각 리소스를 정의해서 사용하는 방법을 이해할 수 있다고 생각한다. 물론 API Gateway도 Terraform도 계속 발전하고 있으므로 변경사항에 대한 추적도 필요하긴 하다.
다음은 infrastructure/github-status.tf
파일의 전체 내용이다.
// API 정의
resource "aws_api_gateway_rest_api" "status_api" {
name = "StatusAPI"
description = "상태를 조회하는 API"
}
// API 리소스 정의 (/check 같은 경로)
resource "aws_api_gateway_resource" "status_api_resource_check" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
parent_id = "${aws_api_gateway_rest_api.status_api.root_resource_id}"
path_part = "check"
}
// API 리소스의 메서드 정의 (GET, POST 등)
resource "aws_api_gateway_method" "status_api_resource_check_post" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "POST"
authorization = "NONE"
}
// API와 Lambda 통합
resource "aws_api_gateway_integration" "status_api_resource_check_post" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
type = "AWS"
integration_http_method = "POST"
uri = "arn:aws:apigateway:${var.aws_region}:lambda:path/2015-03-31/functions/${var.apex_function_github-status}/invocations"
}
// API 배포
resource "aws_api_gateway_deployment" "status_api_test" {
depends_on = ["aws_api_gateway_integration.status_api_resource_check_post"]
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
stage_name = "test"
stage_description = "${timestamp()}"
description = "Deployed at ${timestamp()}"
}
// Lambda에 호출할 권리
resource "aws_lambda_permission" "status_api_resource_check_post" {
statement_id = "AllowInvokeFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = "${var.apex_function_github-status_name}"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_deployment.status_api_test.execution_arn}/${aws_api_gateway_integration.status_api_resource_check_post.integration_http_method}${aws_api_gateway_resource.status_api_resource_check.path}"
}
// API 메서드 설정
resource "aws_api_gateway_method_settings" "status_api_resource_check_post" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
stage_name = "${aws_api_gateway_deployment.status_api_test.stage_name}"
method_path = "${aws_api_gateway_resource.status_api_resource_check.path_part}/${aws_api_gateway_method.status_api_resource_check_post.http_method}"
settings {
metrics_enabled = true
logging_level = "INFO"
}
}
// 200 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_200" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_200" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_200.status_code}"
response_templates = {
"application/json" = ""
}
}
// 400 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_400" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "400"
response_models = {
"application/json" = "Error"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_400" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_400.status_code}"
selection_pattern = ".*BadRequest.*"
response_templates = {
"application/json" = <<EOF
#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))
{
"message": "$errorMessageObj.message",
"stack": "$errorMessageObj.stackTrace",
"requestId": "$errorMessageObj.requestId"
}
EOF
}
}
// 500 응답 매핑
resource "aws_api_gateway_method_response" "status_api_resource_check_post_500" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "500"
response_models = {
"application/json" = "Error"
}
}
resource "aws_api_gateway_integration_response" "status_api_resource_check_post_500" {
rest_api_id = "${aws_api_gateway_rest_api.status_api.id}"
resource_id = "${aws_api_gateway_resource.status_api_resource_check.id}"
http_method = "${aws_api_gateway_method.status_api_resource_check_post.http_method}"
status_code = "${aws_api_gateway_method_response.status_api_resource_check_post_500.status_code}"
selection_pattern = ".*InternalServerError.*"
response_templates = {
"application/json" = <<EOF
#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))
{
"message": "$errorMessageObj.message",
"stack": "$errorMessageObj.stackTrace",
"requestId": "$errorMessageObj.requestId"
}
EOF
}
}
저도 apex사용하면서 terraform도 같이 설정해서 배포하고 관리하는 것에 대해서 학습을 하고 있는 중인데
정훈님이 올려 주신 글이 도움이 많이 되네요.
감사합니다~.
잘 지내시죠? ㅎㅎ Terraform은 완전히 다른 도구라서 apex만 처음 쓸때 어렵긴 하더라고요.