hclconfig

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2022 License: MIT Imports: 16 Imported by: 2

README

hclconfig

Documentation Latest GitHub tag Github Actions test Go Report Card License

go utility package for loading HCL(HashiCorp configuration language) files


Features

  • Implicit variables
  • Built-in functions using standard libraries
  • Interfaces to implement additional restrictions

Requirements

  • Go 1.17 or higher. support the 3 latest versions of Go.

See godoc.org/github.com/mashiike/hclconfig.


Installation

$ go get -u github.com/mashiike/hclconfig

Usage

config/config.hcl

io_mode = "readonly"

service "http" "hoge" {
  addr = "http://127.0.0.1"
  port = 8080
}
package main

import (
	"github.com/mashiike/hclconfig"
)

type Config struct {
	IOMode   string          `hcl:"io_mode"`
	Services []ServiceConfig `hcl:"service,block"`
}

type ServiceConfig struct {
	Type string `hcl:"type,label"`
	Name string `hcl:"name,label"`
	Addr string `hcl:"addr"`
	Port int    `hcl:"port"`
}

func main() {
	var cfg Config
	if err := hclconfig.Load(&cfg, "./config"); err != nil {
		panic(err)
	}
	//...
}
Implicit variables

For example, the following statements are possible

config/config.hcl

io_mode = "readonly"

service "http" "hoge" {
  addr = "http://127.0.0.1"
  port = 8080
}

service "http" "fuga" {
  addr = "http://127.0.0.1"
  port = service.http.hoge.port + 1
}

This is the ability to refer to other blocks and attributes as implicit variables.
The evaluation of this implicit variable is done recursively. Up to 100 nests can be evaluated.

Built-in functions

You can use the same functions that you use most often.

For example, you can use the must_env or env functions to read environment variables. To read from a file, you can use the file function.

env   = must_env("ENV")
shell = env("SHELL", "bash")
text  = file("memo.txt")

You can also use other functions from "github.com/zclconf/go-cty/cty/function/stdlib" such as jsonencode and join.

Additional restrictions

If the following interfaces are met, functions can be called after decoding to implement additional restrictions.

type Restrictor interface {
	Restrict(bodyContent *hcl.BodyContent, ctx *hcl.EvalContext) hcl.Diagnostics
}

example code

package main

import (
	"github.com/hashicorp/hcl/v2"
	"github.com/mashiike/hclconfig"
)

type Config struct {
	IOMode   string          `hcl:"io_mode"`
	Services []ServiceConfig `hcl:"service,block"`
}

type ServiceConfig struct {
	Type string `hcl:"type,label"`
	Name string `hcl:"name,label"`
	Addr string `hcl:"addr"`
	Port int    `hcl:"port"`
}

func (cfg *Config) Restrict(content *hcl.BodyContent, ctx *hcl.EvalContext) hcl.Diagnostics {
	var diags hcl.Diagnostics
	if cfg.IOMode != "readwrite" && cfg.IOMode != "readonly" {
		diags = append(diags, hclconfig.NewDiagnosticError(
			"Invalid io_mode",
			"Possible values for io_mode are readwrite or readonly",
			hclconfig.AttributeRange(content, "io_mode"),
		))
	}
	diags = append(diags, hclconfig.RestrictUniqueBlockLabels(content)...)
	return diags
}

func main() {
	var cfg Config
	if err := hclconfig.Load(&cfg, "./config"); err != nil {
		panic(err)
	}
	//...
}

In this case, if anything other than readonly and readwrite is entered in the IOMode, the following message is output.

io_mode = "public"

service "http" "hoge" {
  addr = "http://127.0.0.1"
  port = 8080
}

service "http" "hoge" {
  addr = "http://127.0.0.1"
  port = 8080
}
Error: Invalid io_mode

  on config/config.hcl line 1:
   1: io_mode = "public"

Possible values for io_mode are readwrite or readonly

Error: Duplicate service "http" configuration

  on config/config.hcl line 8, in service "http" "hoge":
   8: service "http" "hoge" {

A http service named "hoge" was already declared at config/config.hcl:3,1-22. service names must unique per type in a configuration

LICENSE

MIT

Documentation

Overview

Example
package main

import (
	"fmt"
	"os"

	"github.com/mashiike/hclconfig"
)

func main() {
	os.Setenv("HCLCONFIG_VAR", "hoge")
	type ExampleConfig struct {
		Value  string `hcl:"value"`
		Groups []*struct {
			Type  string `hcl:"type,label"`
			Name  string `hcl:"name,label"`
			Value int    `hcl:"value"`
		} `hcl:"group,block"`
	}
	var cfg ExampleConfig
	err := hclconfig.LoadWithBytes(&cfg, "config.hcl", []byte(`
value = must_env("HCLCONFIG_VAR")

group "type1" "default" {
	value = 1
}

group "type2" "default" {
	value = group.type1.default.value + 1
}
	`))
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("value = ", cfg.Value)
	for _, g := range cfg.Groups {
		fmt.Printf("group.%s.%s.value = %v\n", g.Type, g.Name, g.Value)
	}
}
Output:

value =  hoge
group.type1.default.value = 1
group.type2.default.value = 2

Index

Examples

Constants

This section is empty.

Variables

View Source
var EnvFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name:        "key",
			Type:        cty.String,
			AllowMarked: true,
		},
		{
			Name:      "default",
			Type:      cty.String,
			AllowNull: true,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		keyArg, keyMarks := args[0].Unmark()
		key := keyArg.AsString()
		if value := os.Getenv(key); value != "" {
			return cty.StringVal(value).WithMarks(keyMarks), nil
		}
		if args[1].IsNull() {
			return cty.StringVal("").WithMarks(keyMarks), nil
		}
		return cty.StringVal(args[1].AsString()).WithMarks(keyMarks), nil
	},
})
View Source
var MustEnvFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name:        "key",
			Type:        cty.String,
			AllowMarked: true,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		keyArg, keyMarks := args[0].Unmark()
		key := keyArg.AsString()
		value := os.Getenv(key)
		if value == "" {
			err := function.NewArgError(0, fmt.Errorf("env `%s` is not set", key))
			return cty.UnknownVal(cty.String), err
		}
		return cty.StringVal(value).WithMarks(keyMarks), nil
	},
})

Functions

func AttributeRange

func AttributeRange(content *hcl.BodyContent, tagName string) *hcl.Range

func DefaultDiagnosticOutput

func DefaultDiagnosticOutput(w io.Writer)

DefaultDiagnosticOutput specifies the standard diagnostic output destination. If a separate DiagnosticWriter is specified, that setting takes precedence.

func DiagnosticWriter

func DiagnosticWriter(w hcl.DiagnosticWriter)

DiagnosticWriter sets up a Writer to write the diagnostic when an error occurs in the Loader.

func Functions

func Functions(functions map[string]function.Function)

Functions adds functions used during HCL decoding.

func Load

func Load(cfg interface{}, paths ...string) error

Load considers `paths` as a configuration file county written in HCL and reads *.hcl and *.hcl.json. and assigns the decoded values to the `cfg` values.

func LoadWithBody

func LoadWithBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics

LoadWithBody assigns a value to `val` using a parsed hcl.Body and hcl.EvalContext. mainly used to achieve partial loading when implementing Restrict functions.

func LoadWithBytes

func LoadWithBytes(cfg interface{}, filename string, src []byte) error

func NewDiagnosticError

func NewDiagnosticError(summary string, detail string, r *hcl.Range) *hcl.Diagnostic

NewDiagnosticError generates a new diagnostic error.

func NewDiagnosticWarn

func NewDiagnosticWarn(summary string, detail string, r *hcl.Range) *hcl.Diagnostic

NewDiagnosticError generates a new diagnostic warn.

func NewEvalContext

func NewEvalContext() *hcl.EvalContext

NewEvalContext creates a new evaluation context.

func RestrictUniqueBlockLabels

func RestrictUniqueBlockLabels(content *hcl.BodyContent) hcl.Diagnostics

RestrictUniqueBlockLabels implements the restriction that labels for each Block be unique.

func Variables

func Variables(variables map[string]cty.Value)

Variables adds variables used during HCL decoding.

Types

type DiagnosticWriterFunc

type DiagnosticWriterFunc func(diag *hcl.Diagnostic) error

DiagnosticWriterFunc is an alias for a function that specifies how to output diagnostics.

func (DiagnosticWriterFunc) WriteDiagnostic

func (f DiagnosticWriterFunc) WriteDiagnostic(diag *hcl.Diagnostic) error

func (DiagnosticWriterFunc) WriteDiagnostics

func (f DiagnosticWriterFunc) WriteDiagnostics(diags hcl.Diagnostics) error

type Loader

type Loader struct {
	// contains filtered or unexported fields
}

Loader represents config loader.

func New

func New() *Loader

New creates a Loader instance.

func (*Loader) DefaultDiagnosticOutput

func (l *Loader) DefaultDiagnosticOutput(w io.Writer)

DefaultDiagnosticOutput specifies the standard diagnostic output destination. If a separate DiagnosticWriter is specified, that setting takes precedence.

func (*Loader) DiagnosticWriter

func (l *Loader) DiagnosticWriter(w hcl.DiagnosticWriter)

DiagnosticWriter sets up a Writer to write the diagnostic when an error occurs in the Loader.

func (*Loader) Functions

func (l *Loader) Functions(functions map[string]function.Function)

Functions adds functions used during HCL decoding.

func (*Loader) Load

func (l *Loader) Load(cfg interface{}, paths ...string) error

Load considers `paths` as a configuration file county written in HCL and reads *.hcl and *.hcl.json. and assigns the decoded values to the `cfg` values.

func (*Loader) LoadWithBody

func (l *Loader) LoadWithBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics

LoadWithBody assigns a value to `val` using a parsed hcl.Body and hcl.EvalContext. mainly used to achieve partial loading when implementing Restrict functions.

func (*Loader) LoadWithBytes

func (l *Loader) LoadWithBytes(cfg interface{}, filename string, src []byte) error

func (*Loader) NewEvalContext

func (l *Loader) NewEvalContext() *hcl.EvalContext

NewEvalContext creates a new evaluation context.

func (*Loader) Variables

func (l *Loader) Variables(variables map[string]cty.Value)

Variables adds variables used during HCL decoding.

type Restrictor

type Restrictor interface {
	Restrict(bodyContent *hcl.BodyContent, ctx *hcl.EvalContext) hcl.Diagnostics
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL