MigrationsController missing environment consistency validation
RCA: Migration request failed from evergreen — ARG10001 (facility environments mismatch)
Overview#
What Happened#
2026-06-17 11:16~11:27 KST 사이, cupixworks-api (us-west-2 production)의 backoffice 관리자 migration 엔드포인트(POST /api/v1/admin/migrations)에서 7회 연속 502 BadGateway가 발생했다. Tesla가 evergreen migration 서비스에 migration 등록 요청을 보냈으나, evergreen이 source facility와 destination facility의 environment가 다르다는 이유로 ARG10001을 반환하여 모든 요청이 실패했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::BadGateway (재발생) / 원인은 evergreen이 반환한 Cupix::Errors::Parameter (ARG10001) |
| exception.message | Source and destination facility environments are different |
| top_frame | app/operations/migration_operation.rb:64 (MigrationOperation.create_migration_request) |
| http_status | 502 |
| endpoint | POST /api/v1/admin/migrations (Api::V1::Admin::MigrationsController#create) |
| deploy | production-us-west-2-20260613T0358Z0-9443a6d8-cupixworks (event at 11:27), production-us-west-2-20260616T1952Z0-9443a6d8-cupixworks (event at 11:25) |
| env | production / us-west-2 / tenant=cupix |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| Backoffice / CSM (Admin Migrations) | 7 | 운영자가 backoffice UI에서 시도한 facility/record migration 요청이 503/502로 실패 — migration 작업이 시작되지 않음 |
End-user 영향은 없으며, CSM(운영) 워크플로우에 한정된다.
Timeline#
- 2026-06-17 11:16 KST — 첫 번째 실패 (
request_id=fca2f63f-…) - 2026-06-17 11:16~11:17 KST — 4건 연속 실패 (
6e7d70a5-…,d325a27a-…,0d0b08b5-…) - 2026-06-17 11:22 KST — 2건 추가 실패 (
983e99db-…,c9d6d996-…) - 2026-06-17 11:25 KST —
9106e2b1-…실패 (clusterlast_seen) - 2026-06-17 11:27 KST —
7d1926ef-…실패 (Datadog에서 cluster 외 동일 패턴 1건 추가 확인) - 이후 동일 fingerprint 재발 없음 — operator가 시도를 중단한 것으로 보임 (시도 횟수만 7~8회)
Error Log#
Migration request failed from evergreen - {"result":{"code":"ARG10001","type":"Cupix::Errors::Parameter","reason":"Source and destination facility environments are different","message":"The facility environments between source and destination do not match. Migration cannot be performed."}}
Impact#
- Service:
cupixworks-api - 발생 횟수: 7
- 최초 발생: 2026-06-17 11:16 KST
- 최근 발생: 2026-06-17 11:25 KST (Datadog에는 11:27 KST 1건 추가 존재)
Root Cause Summary#
Backoffice의 admin migration 엔드포인트(Api::V1::Admin::MigrationsController#create)는 요청자가 보낸 params[:environment]를 그대로 evergreen에 전달한다. Tesla 측 검증 로직(validate_migration_params)은 단순히 값이 enum(development|dev|qa|stage|production)에 속하는지만 확인하고, source facility가 실제로 속한 환경(이 instance의 Rails.env)과 일치하는지 검증하지 않는다. 운영자가 backoffice UI에서 source facility의 환경과 다른 destination 조합을 입력한 결과, evergreen이 source/destination facility의 환경 mismatch를 감지해 ARG10001을 반환했다. Tesla는 이를 Cupix::Errors::BadGateway로 재포장하여 502로 응답했다. 즉 client(operator) 입력 오류이나, 서버 측 사전 검증이 없어 evergreen 라운드트립 후에야 실패하며 동일한 잘못된 입력으로 7회 연속 재시도가 발생했다.
Technical Analysis#
Code Path#
- Backoffice →
POST /api/v1/admin/migrations - Entry point:
Api::V1::Admin::MigrationsController#create(app/controllers/api/v1/admin/migrations_controller.rb:6-22) - Pre-call validation:
validate_migration_params(app/controllers/api/v1/admin/migrations_controller.rb:67-118) —environment값이 enum 멤버인지만 확인 MigrationOperation.create_migration_request호출 (app/operations/migration_operation.rb:4-67) — body 조립 후 evergreenPOST /api/v1/migrations호출- Evergreen이
ARG10001 Cupix::Errors::Parameter응답 →RestClient::Exception발생 - Failure point:
app/operations/migration_operation.rb:64-65— error 로깅 후Cupix::Errors::BadGateway(BG10001)로 재발생
def create
response = MigrationOperation.create_migration_request(
current_user: current_user,
source_model: @model,
type: params[:type],
tenant: params[:tenant],
region: params[:region],
environment: params[:environment], # client 입력값을 그대로 전달
model_name: params[:model_name],
model_id: params[:model_id],
selected_models: params[:sources],
source_auth_token: params[:source_auth_token],
destination_auth_token: params[:destination_auth_token]
)
render_json 200, { migration_id: response['result']['data']['id'] }
end
# Validate environment
unless %w[development dev qa stage production].include?(params[:environment])
raise Cupix::Errors::Parameter.new(code: 'ARG10000', reason: "environment must be one of: development, dev, qa, stage, production (received: '#{params[:environment]}')")
end
# 참고: 이 validation은 enum 검사만 수행할 뿐, Rails.env 또는 source facility의 environment와 일치하는지는 검사하지 않는다.
begin
response = Cupix::HttpClient.post("#{$EVERGREEN[:api_endpoint]}/api/v1/migrations?fields=id", body, { 'x-cupix-auth': $EVERGREEN[:api_key] })
JSON.parse(response)
rescue RestClient::Exception => e
Cupix::Logger.error("Migration request failed from evergreen - #{e.response}", class: self.class.name, function: __method__, error: e.response)
raise Cupix::Errors::BadGateway.new(code: 'BG10001', reason: 'Migration request failed from evergreen', message: JSON.parse(e.response))
end
기대 동작: client가 잘못된 environment 조합을 보냈을 때 사전 검증으로 4xx(ARG)를 즉시 응답.
실제 동작: enum 검사만 통과하고 그대로 evergreen에 전달 → evergreen 호출 실패 → 502 BadGateway. 결과적으로 클라이언트 입력 오류가 server-side 5xx로 노출되어 cluster 분류상 error로 누적됨.
Log Evidence#
사용 쿼리:
service:cupixworks-api "Migration request failed from evergreen"
service:cupixworks-api @request_id:7d1926ef-22a3-45fd-bf48-5a7bd7ed9c18
service:cupixworks-api @request_id:9106e2b1-32e4-4e42-96ea-cdb3bd3d4946
같은 request_id로 묶인 두 줄 — operation 레벨의 error 로그와, 그에 이은 controller 응답 로그(502 + BadGateway):
{
"timestamp": "2026-06-17 11:27:42",
"status": "error",
"message": "Migration request failed from evergreen - {\"result\":{\"code\":\"ARG10001\",\"type\":\"Cupix::Errors::Parameter\",\"reason\":\"Source and destination facility environments are different\",\"message\":\"The facility environments between source and destination do not match. Migration cannot be performed.\"}}",
"class": "Class",
"function": "create_migration_request",
"request_id": "7d1926ef-22a3-45fd-bf48-5a7bd7ed9c18"
}
{
"timestamp": "2026-06-17 11:27:42",
"status": "info",
"message": "[502] POST /api/v1/admin/migrations (Api::V1::Admin::MigrationsController#create)",
"error": {
"reason": "Migration request failed from evergreen",
"code": "BG10001",
"message": {
"result": {
"reason": "Source and destination facility environments are different",
"code": "ARG10001",
"type": "Cupix::Errors::Parameter",
"message": "The facility environments between source and destination do not match. Migration cannot be performed."
}
},
"class": "Cupix::Errors::BadGateway"
}
}
7건 모두 동일한 endpoint(/api/v1/admin/migrations)이고, 모두 us-west-2 production / tenant=cupix이며, 약 11분 동안 산발적으로 발생했다. 동일 cluster fingerprint가 이후 재발하지 않았다.
코드 검색 결과, Source and destination facility environments are different 문구는 tesla repo 내에 존재하지 않으므로 evergreen 측에서 비교한 결과 메시지임을 확인했다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Operator가 backoffice에서 source facility 환경과 다른 destination(env/region/tenant) 조합을 입력하여 evergreen이 거부 | 7건 모두 Api::V1::Admin::MigrationsController#create (관리자 전용), 5xx 카운트가 짧은 시간 burst 후 자연 종료, source facility의 환경(Rails.env=production)을 그대로 사용하는 일반 사용자용 Api::V1::MigrationsController#create에서는 동일 에러가 검색되지 않음 |
— | Confirmed |
| H2 | Tesla 코드 내부에 환경 비교 로직 버그가 존재 | 없음 — app/operations/migration_operation.rb는 params를 그대로 body에 넣어 전달할 뿐 비교 로직 없음 |
메시지 문구가 tesla repo 내에 없음 (evergreen에서 생성), Tesla 측은 enum 검증만 수행 | Rejected |
| H3 | 배포 변경(다른 deploy SHA)이 회귀를 유발 | 11:25/11:27 로그에 서로 다른 deploy version 표시(20260613T0358Z0, 20260616T1952Z0) |
동일 deploy SHA(9443a6d8)이며 두 버전 모두에서 발생 → 새 회귀 아님. fingerprint 이전 14일 retention 내에 동일 패턴의 추가 발생 없음 |
Rejected |
| H4 | Evergreen 서비스 장애(false positive 거부) | evergreen이 일관된 메시지로 거부 | 다른 migration 호출에는 영향 없음(같은 시간대 다른 endpoint 정상), ARG10001은 evergreen의 정상 validation 코드 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
운영자가 잘못된 입력을 시도한 결과이므로 production hot-fix는 불필요하다. 단, 동일 입력으로 7회 재시도가 발생했다는 점에서 backoffice UX와 백엔드 사전 검증을 강화할 가치가 있다.
- 조치 위치:
app/controllers/api/v1/admin/migrations_controller.rb:67-118(validate_migration_params) - 방향: source 모델(
@model)을set_model로 이미 가져온 뒤,@model이 속한 facility의 environment(Rails.env)와params[:environment]가 같은 region/tenant 조합에서 충돌하지 않는지 사전 검사. 환경이 동일해야 하는 시나리오(예: 같은 region/tenant 내 move)와, 다른 환경 간 copy 시나리오를 명확히 구분하여 4xx로 빠르게 실패하게 한다. 결과적으로 evergreen 라운드트립 없이 즉시ARG로 응답되어 5xx 누적이 사라진다.
단기 개선 (1주 이내)#
MigrationOperation.create_migration_request(app/operations/migration_operation.rb:60-66)에서 evergreen이 반환한ARG(4xx-class) 응답을 무조건Cupix::Errors::BadGateway(5xx)로 재포장하지 말고, 응답 코드의 의미에 따라Cupix::Errors::Parameter등 4xx로 매핑하는 것을 검토. operator 입력 오류가 server error로 오인되어 alerting/cluster 노이즈를 유발하는 문제를 줄인다.- Backoffice UI 측 영역: source facility를 선택하면 그 환경/region/tenant를 자동으로 채우거나, mismatch 시 form-level 에러를 표시. (UI 레포는 이 RCA 범위 외)
장기 개선 (재발 방지)#
- Migration 파이프라인 전체에서 environment/region/tenant 조합 매트릭스를 한곳에서 정의하고, Tesla(source)와 evergreen이 같은 정의를 참조하도록 schema/contract를 공유.
- 운영자용 endpoint에서 발생하는 외부 의존(evergreen) 4xx는 별도 status로 분리 logging(
status:warn또는migration.user_error메트릭)하여 cluster fingerprint가 진짜 시스템 장애와 섞이지 않게 한다.
Monitoring#
service:cupixworks-api status:error "Migration request failed from evergreen"
service:cupixworks-api "ARG10001" "Source and destination facility environments are different"
service:cupixworks-api @http.url_details.path:"/api/v1/admin/migrations" @http.status_code:502
추가 권장:
- Backoffice migration 실패 카운트 알림: 위 첫 번째 쿼리 기준 5분 내 5건 이상 발생 시 backoffice/CSM 채널로 알림 (operator 입력 오류 burst 감지).
BG10001코드 발생 빈도 시계열 위젯 (status:error @error.code:BG10001 service:cupixworks-api).
Risk Assessment#
- Risk level: low — operator 입력 오류이며 end-user 영향 없음. 안전 종료(자연 소멸).
- 예상 복잡도: standard — admin controller에 사전 검증 추가는 기존
validate_migration_params패턴과 동일한 위치에 단순 분기 추가로 처리 가능.