LabHub
배우기 러닝패스 코스

Terraform/OpenTofu Fundamentals

The Review Diff Was All Whitespace

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

서식 판정·구조 검사·표현식 콘솔 세 가지를 직접 돌려 보고, 각각이 무엇을 잡고 무엇을 못 잡는지 경계를 확인합니다. 마지막에는 셋을 묶어 커밋 전에 도는 게이트를 손으로 만듭니다.

왜 중요한가

적용해 봐야 아는 것과 적용 전에 알 수 있는 것을 가르는 일이 이 모듈의 주제입니다. 서식은 도구가 정답을 하나로 정해 두었으므로 사람이 논쟁할 필요가 없습니다 — 판정만 하는 모드가 따로 있고 종료 코드가 다르게 나오니 그대로 게이트가 됩니다. 구조 검사는 설정 파일만 읽어 참조가 존재하는지, 필수 인자가 있는지, 타입이 스키마와 맞는지를 봅니다. 상태도 실물도 보지 않으므로 값이 틀린 것은 잡지 못합니다. 그 경계를 모르면 두 가지 사고가 납니다. 하나는 통과했으니 안전하다고 믿는 것이고, 다른 하나는 정적 검사로 잡을 수 있는 것을 계획까지 끌고 가 시간을 버리는 것입니다. 콘솔은 그 사이를 메웁니다 — 헷갈리는 표현식 하나 때문에 적용을 반복하는 대신 한 줄로 값을 확인합니다.

단계

  1. /root/tfb-fmt/src/main.tf/root/tfb-fmt/src/modules/net/main.tf 를 만들되 들여쓰기와 등호 정렬을 일부러 어긋나게 쓰세요. /root/tfb-fmt/src 에서 tofu fmt -check -diff -recursive 를 돌려 출력을 /root/tfb-fmt/fmt-before.txt 에 저장하고, 마지막 줄에 exit=<종료 코드> 를 덧붙이세요.
  2. /root/tfb-fmt/src 에서 tofu fmt -recursive 로 실제로 고치고, 고쳐진 파일 이름 목록을 /root/tfb-fmt/fmt-files.txt 에 저장하세요. 그다음 tofu fmt -check -recursive 를 다시 돌려 출력과 exit=<종료 코드>/root/tfb-fmt/fmt-after.txt 에 저장하세요.
  3. /root/tfb-fmt/broken/main.tf 에 값이 빠진 인자(예: filename = 뒤에 아무것도 없음)를 두어 문법을 깨뜨리세요. 그 디렉터리에서 tofu fmt -check -recursive 를 돌려 출력과 exit=<종료 코드>/root/tfb-fmt/fmt-broken.txt 에 저장하세요. 이 파일은 고치지 말고 그대로 둡니다.
  4. /root/tfb-fmt/invalid/main.tf 에 선언하지 않은 변수를 참조하는 리소스와, 문자열 자리에 목록을 넣은 리소스를 함께 두고 init 하세요. tofu validate -json 출력을 /root/tfb-fmt/validate.json 에 저장하고, 거기서 읽은 값으로 /root/tfb-fmt/validate-summary.txterrors=<오류 개수>summaries=<요약들을 사전순으로 콤마로 이은 것> 두 줄을 적으세요.
  5. /root/tfb-fmt/blind/main.tf 에 기본값 없는 변수 seed_path 와, 그 경로를 file() 로 읽어 파일을 만드는 리소스를 두고 init 하세요. tofu validate 는 통과하는데 tofu plan 은 두 가지 이유로 실패합니다. 값을 주지 않은 plan 과 없는 경로를 준 plan 을 각각 돌려 뒤쪽 출력을 /root/tfb-fmt/blind-plan.txt 에 저장하고, /root/tfb-fmt/blind-report.txtvalidate=<종료 코드>, plan_novar=<종료 코드>, plan_missing=<종료 코드> 세 줄을 적으세요. 없는 경로로는 ./inputs/seed.txt 를 주고, 그 파일은 만들지 마세요.
  6. /root/tfb-fmt/src 를 init 하고 /root/tfb-fmt/console-in.txt 에 네 줄의 표현식을 적으세요 — join("-", [var.env, var.app, "01"]), length(toset(["a", "a", "b"])), "5" + 5, 1 == "1". 그 파일을 tofu console 에 흘려 넣어 결과를 /root/tfb-fmt/console-out.txt 에 저장하세요.
  7. /root/tfb-fmt/src/name.tflocal.name 을 6단계 첫 표현식과 똑같이 정의하고, 그 값을 한 줄로 쓰는 local_file.name 으로 name.txt 를 만드세요. 새 파일도 서식이 맞아야 하고(fmt -check -recursive 가 0), validate 가 통과해야 하며, apply 까지 마치세요.
  8. /root/tfb-fmt/precheck.sh 를 만드세요. 첫 인자로 받은 디렉터리에 대해 서식 판정을 먼저 하고 통과하면 구조 검사를 합니다. 서식에서 걸리면 fmt 한 낱말을 찍고 0 이 아닌 코드로, 구조에서 걸리면 validate 를 찍고 0 이 아닌 코드로, 둘 다 통과하면 ok 를 찍고 0 으로 끝나야 합니다. /root/tfb-fmt/src 에 돌린 결과를 /root/tfb-fmt/precheck-src.txt 에, /root/tfb-fmt/broken 에 돌린 결과를 /root/tfb-fmt/precheck-broken.txt 에 저장하고 각 파일 끝에 exit=<종료 코드> 를 덧붙이세요.

참고

서식이 어긋난 트리를 찾아낸다

/root/tfb-fmt/src/main.tf/root/tfb-fmt/src/modules/net/main.tf 를 만들되 들여쓰기와 등호 정렬을 일부러 어긋나게 쓰세요. /root/tfb-fmt/src 에서 tofu fmt -check -diff -recursive 를 돌려 출력을 /root/tfb-fmt/fmt-before.txt 에 저장하고, 마지막 줄에 exit=<종료 코드> 를 덧붙이세요.

이 명령은 파일을 고치지 않고 판정만 합니다. 고칠 것이 있을 때의 종료 코드가 0 과 다르다는 점이 게이트로 쓸 수 있는 이유입니다. -diff 는 무엇을 어떻게 고칠지 보여 줍니다.

고치고 다시 확인한다

/root/tfb-fmt/src 에서 tofu fmt -recursive 로 실제로 고치고, 고쳐진 파일 이름 목록을 /root/tfb-fmt/fmt-files.txt 에 저장하세요. 그다음 tofu fmt -check -recursive 를 다시 돌려 출력과 exit=<종료 코드>/root/tfb-fmt/fmt-after.txt 에 저장하세요.

고치는 모드는 바꾼 파일의 이름만 한 줄씩 찍습니다. 하위 디렉터리까지 도는 옵션이 없으면 루트의 파일만 봅니다 — 둘 다 목록에 들어와야 합니다.

문법이 깨진 파일은 서식 판정과 종료 코드가 다르다

/root/tfb-fmt/broken/main.tf 에 값이 빠진 인자(예: filename = 뒤에 아무것도 없음)를 두어 문법을 깨뜨리세요. 그 디렉터리에서 tofu fmt -check -recursive 를 돌려 출력과 exit=<종료 코드>/root/tfb-fmt/fmt-broken.txt 에 저장하세요. 이 파일은 고치지 말고 그대로 둡니다.

서식을 고치려면 먼저 파일을 읽을 수 있어야 합니다. 읽을 수 없을 때의 종료 코드는 '고칠 것이 있다' 와 또 다릅니다. 게이트를 만들 때 이 둘을 한 덩어리로 다뤄도 되는지 생각해 보세요.

validate 가 잡는 것 — 참조·필수 인자·타입

/root/tfb-fmt/invalid/main.tf 에 선언하지 않은 변수를 참조하는 리소스와, 문자열 자리에 목록을 넣은 리소스를 함께 두고 init 하세요. tofu validate -json 출력을 /root/tfb-fmt/validate.json 에 저장하고, 거기서 읽은 값으로 /root/tfb-fmt/validate-summary.txterrors=<오류 개수>summaries=<요약들을 사전순으로 콤마로 이은 것> 두 줄을 적으세요.

validate 는 값이 아니라 구조를 봅니다. 참조가 존재하는지, 필수 인자가 있는지, 넣은 값의 타입이 스키마와 맞는지입니다. -json 을 쓰면 사람이 아니라 CI 가 읽을 수 있습니다.

validate 가 못 잡는 것 — 값

/root/tfb-fmt/blind/main.tf 에 기본값 없는 변수 seed_path 와, 그 경로를 file() 로 읽어 파일을 만드는 리소스를 두고 init 하세요. tofu validate 는 통과하는데 tofu plan 은 두 가지 이유로 실패합니다. 값을 주지 않은 plan 과 없는 경로를 준 plan 을 각각 돌려 뒤쪽 출력을 /root/tfb-fmt/blind-plan.txt 에 저장하고, /root/tfb-fmt/blind-report.txtvalidate=<종료 코드>, plan_novar=<종료 코드>, plan_missing=<종료 코드> 세 줄을 적으세요. 없는 경로로는 ./inputs/seed.txt 를 주고, 그 파일은 만들지 마세요.

정적 검사는 설정 파일만 읽습니다. 변수에 어떤 값이 들어올지, 그 경로에 파일이 있는지는 설정 파일 밖의 사실이라 알 수 없습니다. 그래서 validate 통과는 '적용해도 된다' 는 뜻이 아닙니다.

표현식은 콘솔에서 한 줄로 확인한다

/root/tfb-fmt/src 를 init 하고 /root/tfb-fmt/console-in.txt 에 네 줄의 표현식을 적으세요 — join("-", [var.env, var.app, "01"]), length(toset(["a", "a", "b"])), "5" + 5, 1 == "1". 그 파일을 tofu console 에 흘려 넣어 결과를 /root/tfb-fmt/console-out.txt 에 저장하세요.

콘솔은 지금 디렉터리의 변수와 locals 를 그대로 읽습니다. 뒤 두 줄은 일부러 헷갈리는 것입니다 — 더하기에서 일어나는 변환과 같다 비교에서 일어나지 않는 변환이 다릅니다.

확인한 표현식을 설정에 넣는다

/root/tfb-fmt/src/name.tflocal.name 을 6단계 첫 표현식과 똑같이 정의하고, 그 값을 한 줄로 쓰는 local_file.name 으로 name.txt 를 만드세요. 새 파일도 서식이 맞아야 하고(fmt -check -recursive 가 0), validate 가 통과해야 하며, apply 까지 마치세요.

콘솔에서 확인한 표현식을 그대로 옮기면 값이 맞는지 다시 고민할 필요가 없습니다. 파일을 새로 만들었으니 서식 판정도 다시 통과해야 합니다 — 게이트를 만들기 전에 몸에 익혀 둘 순서입니다.

커밋 전에 도는 정적 검사 게이트를 만든다

/root/tfb-fmt/precheck.sh 를 만드세요. 첫 인자로 받은 디렉터리에 대해 서식 판정을 먼저 하고 통과하면 구조 검사를 합니다. 서식에서 걸리면 fmt 한 낱말을 찍고 0 이 아닌 코드로, 구조에서 걸리면 validate 를 찍고 0 이 아닌 코드로, 둘 다 통과하면 ok 를 찍고 0 으로 끝나야 합니다. /root/tfb-fmt/src 에 돌린 결과를 /root/tfb-fmt/precheck-src.txt 에, /root/tfb-fmt/broken 에 돌린 결과를 /root/tfb-fmt/precheck-broken.txt 에 저장하고 각 파일 끝에 exit=<종료 코드> 를 덧붙이세요.

순서가 중요합니다. 읽을 수 없는 파일이 섞여 있으면 구조 검사도 의미가 없으니 서식 판정이 먼저입니다. 판정만 하고 고치지 않는 옵션을 써야 게이트가 저장소를 바꾸지 않습니다.