การเขียน Kubernetes Controller, Part 4 — Spec แรกของ Controller
จาก Part ที่แล้ว ที่พูดเกี่ยวกับการใช้คุณสมบัติของ Controller มาเพื่อแก้ปัญหาความเป็นอัตโนมัติในการจัดการ Terraform ใน Part นี้เราก็จะมาเริ่มดู Spec แรกกัน
Spec ที่ใช้เขียนอธิบายพฤติกรรมของ Terraform Controller เมื่อมี Terraform Object เข้าไปในระบบนั้นเป็น Test Case ที่ตอนแรกเขียนด้วย Ginkgo และ Gomega ต่อมา Maintainer ของ Flux มาเล่าให้ฟังว่า Ginkgo นั้นสร้างความลำบากให้กับการเขียน Test เพราะ Logic การ Execute มันซับซ้อนเกินจำเป็น แต่ผมเจอว่า Ginkgo มีการใช้ It(“”) ใช้ By(“”) ในการเพิ่ม Expressiveness ของ Test ได้ดีก็เลยยังอยากได้ฟีเจอร์นี้อยู่
ส่วน Gomega ทุกคนลงความเห็นว่าดี ตัวอย่าง เช่น Eventually(…) มีประโยชน์มากในการ Test ตัว Status ของ Kubernetes Object เป็นต้น
ก็เลยแกะดู Ginkgo เจอว่า It(“”) และ By(“”) เป็นแค่ fmt.Fprintf ก็เลยลอกเอามาใช้แบบดื้อ ๆ แล้วก็ลบ Ginkgo ออกจาก go.mod ไป
ตัว Spec แรกที่ได้ เลยมีหน้าตาแบบนี้
มันไม่ได้ Generate แบบ Automatic นะครับ อันนี้ Print ออกมาปนกับ Log ของ Controller แล้วก็จัด Format ด้วยมือ ไม่อยากเสียเวลาไปเขียน Pretty Printer เลยทำแบบนี้ไปก่อน
ตัว Executable ของ Spec (Test Case) จริง ๆ อยู่ที่นี่
คราวนี้ก็มาไล่ว่า Spec มันทำอะไรบ้าง เราจะคุยเป็นส่วน ๆ แบ่งเป็นพาร์ทด้านการออกแบบ, พาร์ทด้านกลไกทาง Kubernetes Machinery, และพาร์ทด้าน GitOps
พาร์ทด้านการออกแบบ API
จาก Spec เราได้ API หน้าตาแบบนี้ คือมีแค่ .spec.approvePlan เป็นโหมด เรียกว่า “auto” คือทำทั้ง Plan และ Apply ตัว Terraform Resource และ .status.availableOutputs ที่เป็นการลิสต์ออกมาว่ามี output อะไรที่สามารถดึงออกมาได้บ้างจากผลการ reconcile ตัว Terraform
สำหรับส่วนอื่น ๆ ก็จะเป็นการเชื่อมต่อเข้ากับ Flux source ในกรณีนี้คือ GitRepositry และทำการ reconcile ตัว Terraform resource ที่อยู่ในพาธ ./terraform-hello-world-example
พาร์ทด้านกลไกทาง Kubernetes Machinery
ในส่วนของ Kubernetes Machinery เราจะเห็นการใช้ API Machinery หลาย ๆ ส่วน ไม่ว่าจะเป็นการใช้ Controller Runtime Client เพื่อ Create, หรือ Update Object ซึ่งก็คือการเปลี่ยนค่าใน Spec และการ Update Spec ผ่าน API Server เข้าไปเก็บใน ETCD
ส่วนการ Update Status ต้องแยกทำด้วย API อีกชุดซึ่งเป็นกลไกพิเศษที่ต้องทำความเข้าใจไม่งั้นก็จะงงอยู่นานว่าทำไมตั้งค่าใน Status แล้วสั่ง Update ด้วยชุด API สำหรับ Spec แล้วไม่ Work ซักที เป็นต้น
พาร์ทด้าน GitOps
ในเชิงการเปลี่ยน State ไม่มี เพราะกลไกใน Spec นี้เป็นแบบ “auto” เลยยังไม่มีอะไร ใน Spec ตัวอื่นจะมีแง่มุมเชิงการออกแบบที่ต้องคิดเพื่อให้เข้ากับ GitOps Workflow ซึ่งจะเอาไว้เล่าต่อไปครับ
ในเชิงการดึง Artifact มาใช้จาก Source controller จะเป็นขั้นตอนมาตรฐานคือได้ URL มาจาก Status ของ source จากนั้นก็ทำการ Download และ Untar เพื่อเตรียม Directory สำหรับให้ Terraform รัน
ในเชิงการ Emit Event เพื่อให้ Notification Controller ใช้ได้ ตอนนี้มีตรงขั้นตอนการเกิด Error และการ Apply สำหรับ โดยในรายละเอียดการคุยกับ Event Recorder จะเล่าให้ฟังในครั้งถัด ๆ ไปครับ
ในการสร้าง Condition แบบ Ready ที่ตรวจจับได้ด้วย Kustomization Controller เพื่อให้เกิด Health Check ตัว Terraform ตรงนี้มีมาตรฐานอยู่แล้วว่า ถ้า Condition ใน Status เป็น Type แบบ Ready และมีค่า Status เป็น True จะทำให้ Kustomization Controller สรุปว่าตัว object ที่ถูกควบคุมอยู่นั้น (ในกรณีนี้คือ Terraform object) มีสถานะ Healthy ซึ่งถ้าออกแบบให้สอดคล้องกันก็จะทำให้เกิดการ Chain object ได้อย่างน่าสนใจ ซึ่งผมจะเล่าตัว Application ของ Feature นี้ให้ฟังต่อไปครับ
จาก Spec แรกที่เป็นการออกแบบด้วย TDD และได้ฟีเจอร์แรกคือ
ApprovePlan = “auto” ออกมา ก็เริ่มต้นประมาณนี้ครับ
ใน Part ถัดไปก็จะขยับไปที่ Spec อื่น ๆ ที่เกี่ยวข้องกับ GitOps Workflow และ Terraform Workflow มากขึ้นครับ