HCL로 다음 도구 구축
28528 단어 hcltutrigoprogramming
HCL 구성 형식은 Terraform , Vault 및 Nomad 과 같은 모든 놀라운 HasiCorp 도구에서 사용됩니다.
오늘날 대부분의 최신 애플리케이션과 도구는 일반적으로 읽기 쉬운 형식인 YAML을 사용하지만 공백 감도로 인해 많은 문제를 일으킬 수도 있습니다. 반면에 HCL은 명확한 구조와 변수 보간 및 인라인 함수 호출과 같은 추가 기능으로 읽기 쉬운 형식을 제공합니다.
HCL을 구문 분석하는 정말 간단한 예제부터 시작하겠습니다.
task "first_task" {
exec "list_current_dir" {
command = "ls ."
}
exec "list_var_dir" {
command = "ls /var"
}
}
HCL을 구조체에 매핑하기 위해 구조체 태그를 사용할 수 있습니다.
type Config struct {
Tasks []*Task `hcl:"task,block"`
}
type Task struct {
Name string `hcl:"name,label"`
Steps []*ExecStep `hcl:"exec,block"`
}
type ExecStep struct {
Name string `hcl:"name,label"`
Command string `hcl:"command"`
}
func (s *ExecStep) Run() error {
return nil
}
HCL을 디코딩하기 위해
decode
패키지의 hclsimple
함수를 사용할 수 있습니다.import (
"fmt"
"os"
"github.com/hashicorp/hcl/v2/hclsimple"
)
var (
exampleHCL = `
task "first_task" {
exec "list_current_dir" {
command = "ls ."
}
exec "list_var_dir" {
command = "ls /var"
}
}
`
)
func main() {
config := &Config{}
err := hclsimple.Decode("example.hcl", []byte(exampleHCL), nil, config)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, task := range config.Tasks {
fmt.Printf("Task: %s\n", task.Name)
for _, step := range task.Steps {
fmt.Printf(" Step: %s %q\n", step.Name, step.Command)
err = step.Run()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
}
그것은 쉽다!
그러나 다른 단계 유형을 지원하려면 어떻게 해야 합니까? 쉽게 디렉토리를 생성하기 위해
mkdir
를 지원하고 싶다고 가정해 보겠습니다.task "first_task" {
mkdir "build_dir" {
path = "./build"
}
exec "list_var_dir" {
command = "ls /var"
}
}
도구를 실행하면 다음 오류가 발생합니다.
example.hcl:3,4-9: Unsupported block type; Blocks of type "mkdir" are not expected here.
Task 구조체를 업데이트하고 "mkdir"에 대한 차단 목록을 추가할 수 있습니다.
type Task struct {
Name string `hcl:"name,label"`
ExecSteps []*ExecStep `hcl:"exec,block"`
MkdirStep []*MkdirStep `hcl:"mkdir,block"`
}
그러나 두 개의 별도 목록이 있으므로 실행 순서를 잃게 됩니다. 이것은 우리에게 효과가 없을 것입니다.
대체 솔루션으로 구성을 변경하고 단계 유형을 레이블로 만들 수 있습니다.
task "first_task" {
step "mkdir" "build_dir" {
path = "./build/"
}
step "exec" "list_build_dir" {
command = "ls ./build/"
}
}
구성 변경을 구조체에 반영해 보겠습니다.
type Config struct {
Tasks []*Task `hcl:"task,block"`
}
type Task struct {
Name string `hcl:"name,label"`
Steps []*Step `hcl:"step,block"`
}
type Step struct {
Type string `hcl:"type,label"`
Name string `hcl:"name,label"`
Remain hcl.Body `hcl:",remain"`
}
type ExecStep struct {
Command string `hcl:"command"`
}
func (s *ExecStep) Run() error {
return nil
}
type MkdirStep struct {
Path string `hcl:"path"`
}
func (s *MkdirStep) Run() error {
return nil
}
보시다시피 새로운
Step
구조체를 추가하고 Tasks
대신 ExecStep
구조체에서 사용합니다.Step
구조체에는 Remain
라는 추가 필드가 있습니다. 이 필드는 실제 단계 구현의 모든 필드를 캡처하는 데 필요합니다. 나중에 Remain
필드를 사용하여 필드를 실제 단계 구현으로 디코딩하는 방법을 살펴보겠습니다.마지막으로 Step 구현을 실행할 수 있는 새 인터페이스를 추가합니다.
type Runner interface {
Run() error
}
위에서 모든 단계가 Runner 인터페이스를 구현하는 것을 볼 수 있습니다.
이제 구문 분석 코드를 수정해야 합니다.
import (
"fmt"
"os"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsimple"
)
var (
exampleHCL = `
task "first_task" {
step "mkdir" "build_dir" {
path = "./build/"
}
step "exec" "list_build_dir" {
command = "ls ./build/"
}
}
`
)
func main() {
config := &Config{}
err := hclsimple.Decode("example.hcl", []byte(exampleHCL), nil, config)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, task := range config.Tasks {
fmt.Printf("Task: %s\n", task.Name)
for _, step := range task.Steps {
fmt.Printf(" Step: %s %s\n", step.Type, step.Name)
// The actual step implementation
// which implements the Runner interface
var runner Runner
// Determine the step implementation
switch step.Type {
case "mkdir":
runner = &MkdirStep{}
case "exec":
runner = &ExecStep{}
default:
fmt.Printf("Unknown step type %q\n", step.Type)
os.Exit(1)
}
// Decode remaining fields into our step implementation.
diags := gohcl.DecodeBody(step.Remain, nil, runner)
if diags.HasErrors() {
fmt.Println(diags)
os.Exit(1)
}
err = runner.Run()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
}
구문 분석은 switch 문에서 단계 유형을 결정하고 Struct의 인스턴스를 만듭니다. 그런 다음 구조체는 나머지 필드를 디코딩하는
gohcl.DecodeBody(step.Remain, nil, runner)
의 대상입니다.Voilà, 우리는 확장하기 쉬운 작업 실행 엔진을 가지고 있습니다.
자원
Godocs:
기타:
출처 요지:
전체 소스 코드
package main
import (
"fmt"
"os"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsimple"
)
var (
exampleHCL = `
task "first_task" {
step "mkdir" "build_dir" {
path = "./build/"
}
step "exec" "list_build_dir" {
command = "ls ./build/"
}
}
`
)
func main() {
config := &Config{}
err := hclsimple.Decode("example.hcl", []byte(exampleHCL), nil, config)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, task := range config.Tasks {
fmt.Printf("Task: %s\n", task.Name)
for _, step := range task.Steps {
fmt.Printf(" Step: %s %s\n", step.Type, step.Name)
var runner Runner
switch step.Type {
case "mkdir":
runner = &MkdirStep{}
case "exec":
runner = &ExecStep{}
default:
fmt.Printf("Unknown step type %q\n", step.Type)
os.Exit(1)
}
diags := gohcl.DecodeBody(step.Remain, nil, runner)
if diags.HasErrors() {
fmt.Println(diags)
os.Exit(1)
}
err = runner.Run()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
}
type Config struct {
Tasks []*Task `hcl:"task,block"`
}
type Task struct {
Name string `hcl:"name,label"`
Steps []*Step `hcl:"step,block"`
}
type Step struct {
Type string `hcl:"type,label"`
Name string `hcl:"name,label"`
Remain hcl.Body `hcl:",remain"`
}
type ExecStep struct {
Command string `hcl:"command"`
}
func (s *ExecStep) Run() error {
// Implement me
return nil
}
type MkdirStep struct {
Path string `hcl:"path"`
}
func (s *MkdirStep) Run() error {
// Implement me
return nil
}
type Runner interface {
Run() error
}
Reference
이 문제에 관하여(HCL로 다음 도구 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/weakpixel/build-your-next-tool-with-hcl-54po텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)