zoukankan      html  css  js  c++  java
  • hcl v2 golang 使用的一个参考demo

    代码内容来自hashcorp 公司的一个分享,此demo 里边的一些实践很不错,很值得参考(实际上consul,vault,packer,terroform。。。都是值得参考的)

    代码结构

    ├── README.md
    ├── go.mod
    ├── go.sum
    ├── ink.jpg
    ├── main.go
    ├── pet.go
    ├── pet_test.go
    ├── pets.hcl
    └── testdata
        ├── basic.hcl
        ├── function.hcl
        └── variables.hcl

    一个参考配置

    pet "Ink" {
      type = "cat"
      characteristics {
        sound = "${env.CAT_SOUND}s ${random("evilly", "lazily", "sleepily", "gracefully")}"
      }
    }
    pet "Swinney" {
      type = "dog"
      characteristics {
        breed = "Dachshund"
      }
    }

    pet.go 核心处理代码

    简单说明,pet 对于hcl 文件的处理,不是全家桶模式的,而是进行了拆分,基于hcl 实现了一个类似面向对象的处理
    pet 氛围cat 以及dog,所以定义了一个interface pet cat 以及dog 实现了say 以及act 方法,因为每个类型会有自己的
    字段,所以每个字段有自己的hcl golang tag 处理,demo 中没有像我们平时直接定义一个通用struct 的模式,而且定义
    了一个通用的对于不同的地方直接使用hcl 的remain,然后各自的type 处理自己的解析,好处是简化了类型的判断到一个
    复杂而且大的struct 中
    hcl 使用内部函数提供解析,参考

     
    // 创建解析器
    parser := hclparse.NewParser()
    // 基于文件内容,解析hcl content
    srcHCL, diag := parser.ParseHCL(src, filename)
    // 解析部分定义的hcl 内容,可以灵活扩展
    gohcl.DecodeBody(srcHCL.Body, evalContext, petsHCL)

    pet hcl dsl 定义
    拆分为了PetHCLBodies 以及CharacteristicsHCL 好处是基于业务模型拆分,每部分拥有自己的实现

     
    type PetsHCL struct {
        PetHCLBodies []*struct {
            Name               string `hcl:",label"`
            Type               string `hcl:"type"`
            CharacteristicsHCL *struct {
                HCL hcl.Body `hcl:",remain"`
            } `hcl:"characteristics,block"`
        } `hcl:"pet,block"`
    }

    核心读取部分
    实现了env 以及function 的功能

     
    // ReadConfig decodes the HCL file at filename into a slice of Pets and returns
    // it.
    func ReadConfig(filename string) ([]Pet, error) {
        // First, open a file handle to the input filename.
        input, err := os.Open(filename)
        if err != nil {
            return []Pet{}, fmt.Errorf(
                "error in ReadConfig openin pet config file: %w", err,
            )
        }
        defer input.Close()
        // Next, read that file into a byte slice for use as a buffer. Because HCL
        // decoding must happen in the context of a whole file, it does not take an
        // io.Reader as an input, instead relying on byte slices.
        src, err := ioutil.ReadAll(input)
        if err != nil {
            return []Pet{}, fmt.Errorf(
                "error in ReadConfig reading input `%s`: %w", filename, err,
            )
        }
        // Instantiate an HCL parser with the source byte slice.
        parser := hclparse.NewParser()
        srcHCL, diag := parser.ParseHCL(src, filename)
        if diag.HasErrors() {
            return []Pet{}, fmt.Errorf(
                "error in ReadConfig parsing HCL: %w", diag,
            )
        }
        // Call a helper function which creates an HCL context for use in
        // decoding the parsed HCL.
        evalContext, err := createContext()
        if err != nil {
            return []Pet{}, fmt.Errorf(
                "error in ReadConfig creating HCL evaluation context: %w", err,
            )
        }
        // Start the first pass of decoding. This decodes all pet blocks into
        // a generic form, with a Type field for use in determining whether they
        // are cats or dogs. The configuration in the characteristics will be left
        // undecoded in an hcl.Body. This Body will be decoded into different pet
        // types later, once the context of the Type is known.
        petsHCL := &PetsHCL{}
        if diag := gohcl.DecodeBody(srcHCL.Body, evalContext, petsHCL); diag.HasErrors() {
            return []Pet{}, fmt.Errorf(
                "error in ReadConfig decoding HCL configuration: %w", diag,
            )
        }
        // Iterate through the generic pets, switch on type, then decode the
        // hcl.Body into the correct pet type. This allows "polymorphism" in the
        // pet blocks.
        pets := []Pet{}
        for _, p := range petsHCL.PetHCLBodies {
            switch petType := p.Type; petType {
            case "cat":
                cat := &Cat{Name: p.Name, Sound: defaultCatSound}
                if p.CharacteristicsHCL != nil {
                    if diag := gohcl.DecodeBody(p.CharacteristicsHCL.HCL, evalContext, cat); diag.HasErrors() {
                        return []Pet{}, fmt.Errorf(
                            "error in ReadConfig decoding cat HCL configuration: %w", diag,
                        )
                    }
                }
                pets = append(pets, cat)
            case "dog":
                dog := &Dog{Name: p.Name, Breed: defaultDogBreed}
                if p.CharacteristicsHCL != nil {
                    if diag := gohcl.DecodeBody(p.CharacteristicsHCL.HCL, evalContext, dog); diag.HasErrors() {
                        return []Pet{}, fmt.Errorf(
                            "error in ReadConfig decoding dog HCL configuration: %w", diag,
                        )
                    }
                }
                pets = append(pets, dog)
            default:
                // Error in the case of an unknown type. In the future, more types
                // could be added to the switch to support, for example, fish
                // owners.
                return []Pet{}, fmt.Errorf("error in ReadConfig: unknown pet type `%s`", petType)
            }
        }
        return pets, nil
    }
    // createContext is a helper function that creates an *hcl.EvalContext to be
    // used in decoding HCL. It creates a set of variables at env.KEY
    // (namely, CAT_SOUND). It also creates a function "random(...string)" that can
    // be used to assign a random value in an HCL config.
    func createContext() (*hcl.EvalContext, error) {
        // Extract the sound cats make from the environment, with a default.
        catSound := defaultCatSound
        if os.Getenv(catSoundKey) != "" {
            catSound = os.Getenv(catSoundKey)
        }
        // variables is a list of cty.Value for use in Decoding HCL. These will
        // be nested by using ObjectVal as a value. For istance:
        //   env.CAT_SOUND => "meow"
        variables := map[string]cty.Value{
            environmentKey: cty.ObjectVal(map[string]cty.Value{
                catSoundKey: cty.StringVal(catSound),
            }),
        }
        // functions is a list of cty.Functions for use in Decoding HCL.
        functions := map[string]function.Function{
            "random": function.New(&function.Spec{
                // Params represents required positional arguments, of which random
                // has none.
                Params: []function.Parameter{},
                // VarParam allows a "VarArgs" type input, in this case, of
                // strings.
                VarParam: &function.Parameter{Type: cty.String},
                // Type is used to determine the output type from the inputs. In
                // the case of Random it only accepts strings and only returns
                // strings.
                Type: function.StaticReturnType(cty.String),
                // Impl is the actual function. A "VarArgs" number of cty.String
                // will be passed in and a random one returned, also as a
                // cty.String.
                Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
                    resp := args[rand.Intn(len(args))]
                    return cty.StringVal(resp.AsString()), nil
                },
            }),
        }
        // Return the constructed hcl.EvalContext.
        return &hcl.EvalContext{
            Variables: variables,
            Functions: functions,
        }, nil
    }

    说明

    以上是官方hcl v2 golang 的一个demo 学习还是很值得看看的

    参考资料

    https://github.com/hashicorp/hcl/tree/hcl2
    https://github.com/RussellRollins/pet-sounds

  • 相关阅读:
    【BZOJ-4423】Bytehattan 并查集 + 平面图转对偶图
    【BZOJ-2251】外星联络 后缀数组 + 暴力
    【BZOJ-1046】上升序列 DP + 贪心
    【BZOJ-3667】Rabin_Miller算法 随机化判素数
    【BZOJ-4173】数学 欧拉函数 + 关于余数的变换
    【BZOJ-1692&1640】队列变换 后缀数组 + 贪心
    【BZOJ-1857】传送带 三分套三分
    【BZOJ-1717】Milk Patterns产奶的模式 后缀数组
    【训练记录】后缀数组
    【BZOJ-1031】字符加密Cipher 后缀数组
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/13254284.html
Copyright © 2011-2022 走看看