Terraform 실전 · 모듈에 시험을 붙인다 · 실습
모듈을 고칠 때마다 손으로 확인하다가 하나를 놓쳤다
목표
시험할 수 있는 모양의 모듈을 만들고, 계획만 세우는 값싼 시험과 실제로 만드는 비싼 시험을 각각 쓰고, run 을 이어 붙이고, 막혀야 할 것이 막히는지 시험하고, 실패가 어떤 메시지를 내는지 보고, mock 으로 값만 확인한 뒤, CI 가 부를 한 줄로 묶습니다.
왜 중요한가
재사용하는 모듈은 부르는 곳이 늘어날수록 고치기 무서워집니다. '이 값을 바꾸면 어디가 깨지나' 를 사람이 매번 확인하기 때문입니다. 시험을 붙여 두면 그 확인이 명령 한 줄이 되고, 그때부터 모듈을 고칠 수 있게 됩니다. 중요한 것은 시험을 두 종류로 나눠 쓰는 감각입니다 — 계획만 세우는 시험은 빠르고 값싸서 이름 규칙·조건 분기·기본값처럼 값만 보면 되는 것을 다 덮을 수 있고, 실제로 만드는 시험은 느리지만 '정말 그것이 그렇게 생기는가' 를 확인하는 유일한 방법입니다. 여기에 '막혀야 하는 것이 막히는가' 를 확인하는 시험을 더하면, validation 을 잘못 고쳤을 때 조용히 통과하는 일이 없어집니다. 마지막으로 이 모든 것이 CI 의 종료 코드 하나로 이어져야 값어치가 생깁니다.
단계
1. /root/tfa-test/main.tf 에 var.env(dev·stage·prod 만 허용하는 validation)와 var.replicas(기본 1, 1~10 만 허용)를 받아 out/app-<env>.conf 에 name= 과 replicas= 두 줄을 쓰는 local_file.conf 를 두고, 출력 name·conf_path·replicas 세 개를 선언하세요. init 하고 tofu validate 로 확인합니다.
2. /root/tfa-test/tests/basic.tftest.hcl 에 파일 수준 variables 로 env = "dev" 를 두고, command = plan 인 run "name_is_prefixed" 에서 출력 name 이 app-dev 인지 단언하세요. tofu test 를 이 파일만 걸러 돌리고 출력을 /root/tfa-test/results/basic.txt 에 저장합니다.
3. /root/tfa-test/tests/apply.tftest.hcl 에 env = "stage", replicas = 3 으로 command = apply 인 run "file_is_written" 을 두고, file(output.conf_path) 의 내용이 정확히 name=app-stage 와 replicas=3 두 줄인지 단언하세요. 이 파일만 걸러 돌린 출력을 /root/tfa-test/results/apply.txt 에 저장합니다.
4. /root/tfa-test/tests/chain.tftest.hcl 에 command = apply 인 run "make_dev" 와, 그 run 의 출력을 참조해 값을 정하는 command = plan 인 run "reuse_previous_output" 을 두세요. 두 번째 run 은 출력 name 이 app-prod 인지 단언합니다. 이 파일만 걸러 돌린 출력을 /root/tfa-test/results/chain.txt 에 저장하세요.
5. /root/tfa-test/tests/validation.tftest.hcl 에 env = "qa" 로 도는 run "bad_env_is_rejected" 와 replicas = 99 로 도는 run "too_many_replicas_is_rejected" 를 두고, 각각 expect_failures 로 해당 변수의 validation 이 막는 것이 정상임을 선언하세요. 이 파일만 걸러 돌린 출력을 /root/tfa-test/results/validation.txt 에 저장합니다.
6. /root/tfa-test/tests/fail.tftest.hcl 에 일부러 틀리는 단언(출력 name 이 app-nope 인지)을 두고 tofu test 를 돌리세요. 출력은 /root/tfa-test/results/fail.txt 에, 종료 코드는 /root/tfa-test/results/fail.rc 에 한 줄로 저장합니다. 그다음 그 파일을 /root/tfa-test/broken/fail.tftest.hcl 로 옮겨 시험 묶음이 다시 통과하게 만드세요.
7. /root/tfa-test/tests/mock.tftest.hcl 에 mock_provider "local" {} 를 두고 env = "prod" 로 command = apply 인 run "mocked_apply" 에서 출력 name 이 app-prod 인지 단언하세요. 이 파일만 걸러 돌린 출력을 /root/tfa-test/results/mock.txt 에 저장합니다.
8. /root/tfa-test/ci.sh 를 만드세요. 자기가 놓인 디렉터리로 옮겨 tofu init -input=false 를 돌리고(실패하면 2로 끝남), 이어서 tofu test 를 돌려 출력을 /root/tfa-test/results/ci.txt 에 남기고 시험의 종료 코드를 그대로 돌려줘야 합니다. 만들고 나서 한 번 돌려 0 으로 끝나는지 확인하세요.
참고
- 파드에는 OpenTofu 1.9.0 과 local 프로바이더 미러가 있어 인터넷 없이 돕니다. tofu test 는 오프라인에서 그대로 동작합니다.
- 시험 파일은 현재 디렉터리나 tests 디렉터리에서 찾습니다. 특정 파일만 돌리려면 filter 옵션을 씁니다.
- apply 로 도는 run 은 실제로 만들고 끝나면 도구가 지웁니다. 시험이 남긴 찌꺼기가 없는지 out/ 을 확인해 보세요.
- 흔한 실수: CI 스크립트가 시험의 종료 코드를 삼키는 것. 초록불로 지나가는 파이프라인은 시험이 없는 것보다 나쁩니다.
- [Command: test](https://opentofu.org/docs/cli/commands/test/) · [Command: validate](https://opentofu.org/docs/cli/commands/validate/) · [Input Variables](https://opentofu.org/docs/language/values/variables/)
단계 8개
- 시험을 붙일 대상을 만든다
- 계획만 세우는 값싼 시험
- 실제로 만들어 확인하는 비싼 시험
- 앞 시험의 결과를 다음 시험이 쓴다
- 막혀야 하는 것이 막히는지 시험한다
- 실패하는 시험이 무엇을 보여 주는지 본다
- 프로바이더를 흉내 내어 값만 시험한다
- CI 가 부를 한 줄로 묶는다