syntax

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2024 License: Apache-2.0 Imports: 6 Imported by: 0

README

Alloy configuration syntax

API User documentation

The Alloy configuration syntax is a domain-specific language used by Grafana Alloy to define pipelines.

The syntax was designed with the following goals:

  • Fast: The syntax must be fast so the component controller can quickly evaluate changes.
  • Simple: The syntax must be easy to read and write to minimize the learning curve.
  • Debuggable: The syntax must give detailed information when there's a mistake in the configuration file.

The syntax package is importable as a Go module so other projects can use it.

NOTE: The syntax submodule is versioned separately from the main module, and does not have a stable API.

Example

// Discover Kubernetes pods to collect metrics from.
discovery.kubernetes "pods" {
  role = "pod"
}

// Collect metrics from Kubernetes pods.
prometheus.scrape "default" {
  targets    = discovery.kubernetes.pods.targets
  forward_to = [prometheus.remote_write.default.receiver]
}

// Get an API key from disk.
local.file "apikey" {
  filename  = "/var/data/my-api-key.txt"
  is_secret = true
}

// Send metrics to a Prometheus remote_write endpoint.
prometheus.remote_write "default" {
  endpoint {
    url = "http://localhost:9009/api/prom/push"

    basic_auth {
      username = "MY_USERNAME"
      password = local.file.apikey.content
    }
  }
}

Limitations

The syntax submodule only contains lower level concepts (attributes, blocks, expressions). It does not contain any higher level concepts like components or services.

Documentation

Overview

Package syntax implements a high-level API for decoding and encoding Alloy configuration files. The mapping between Alloy and Go values is described in the documentation for the Unmarshal and Marshal functions.

Lower-level APIs which give more control over configuration evaluation are available in the inner packages. The implementation of this package is minimal and serves as a reference for how to consume the lower-level packages.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNoConversion = value.ErrNoConversion

ErrNoConversion is returned by implementations of ConvertibleFromCapsule and ConvertibleToCapsule when a conversion with a specific type is unavailable.

Returning this error causes Alloy to fall back to default conversion rules.

Functions

func Marshal

func Marshal(v interface{}) ([]byte, error)

Marshal returns the pretty-printed encoding of v as a Alloy configuration file. v must be a Go struct with alloy struct tags which determine the structure of the resulting file.

Marshal traverses the value v recursively, encoding each struct field as a Alloy block or attribute, based on the flags provided to the alloy struct tag.

When a struct field represents an Alloy block, Marshal creates a new block and recursively encodes the value as the body of the block. The name of the created block is taken from the name specified by the alloy struct tag.

Struct fields which represent Alloy blocks must be either a Go struct or a slice of Go structs. When the field is a Go struct, its value is encoded as a single block. When the field is a slice of Go structs, a block is created for each element in the slice.

When encoding a block, if the inner Go struct has a struct field representing a Alloy block label, the value of that field is used as the label name for the created block. Fields used for Alloy block labels must be the string type. When specified, there must not be more than one struct field which represents a block label.

The alloy tag specifies a name, possibly followed by a comma-separated list of options. The name must be empty if the provided options do not support a name being defined. The following provides examples for all supported struct field tags with their meanings:

// Field appears as a block named "example". It will always appear in the
// resulting encoding. When decoding, "example" is treated as a required
// block and must be present in the source text.
Field struct{...} `alloy:"example,block"`

// Field appears as a set of blocks named "example." It will appear in the
// resulting encoding if there is at least one element in the slice. When
// decoding, "example" is treated as a required block and at least one
// "example" block must be present in the source text.
Field []struct{...} `alloy:"example,block"`

// Field appears as block named "example." It will always appear in the
// resulting encoding. When decoding, "example" is treated as an optional
// block and can be omitted from the source text.
Field struct{...} `alloy:"example,block,optional"`

// Field appears as a set of blocks named "example." It will appear in the
// resulting encoding if there is at least one element in the slice. When
// decoding, "example" is treated as an optional block and can be omitted
// from the source text.
Field []struct{...} `alloy:"example,block,optional"`

// Field appears as an attribute named "example." It will always appear in
// the resulting encoding. When decoding, "example" is treated as a
// required attribute and must be present in the source text.
Field bool `alloy:"example,attr"`

// Field appears as an attribute named "example." If the field's value is
// the Go zero value, "example" is omitted from the resulting encoding.
// When decoding, "example" is treated as an optional attribute and can be
// omitted from the source text.
Field bool `alloy:"example,attr,optional"`

// The value of Field appears as the block label for the struct being
// converted into a block. When decoding, a block label must be provided.
Field string `alloy:",label"`

// The inner attributes and blocks of Field are exposed as top-level
// attributes and blocks of the outer struct.
Field struct{...} `alloy:",squash"`

// Field appears as a set of blocks starting with "example.". Only the
// first set element in the struct will be encoded. Each field in struct
// must be a block. The name of the block is prepended to the enum name.
// When decoding, enum blocks are treated as optional blocks and can be
// omitted from the source text.
Field []struct{...} `alloy:"example,enum"`

// Field is equivalent to `alloy:"example,enum"`.
Field []struct{...} `alloy:"example,enum,optional"`

If an alloy tag specifies a required or optional block, the name is permitted to contain period `.` characters.

Marshal will panic if it encounters a struct with invalid alloy tags.

When a struct field represents an Alloy attribute, Marshal encodes the struct value as an Alloy value. The attribute name will be taken from the name specified by the alloy struct tag. See MarshalValue for the rules used to convert a Go value into an Alloy value.

Example
package main

import (
	"fmt"

	"github.com/grafana/alloy/syntax"
)

func main() {
	type Person struct {
		Name     string `alloy:"name,attr"`
		Age      int    `alloy:"age,attr"`
		Location string `alloy:"location,attr,optional"`
	}

	p := Person{
		Name: "John Doe",
		Age:  43,
	}

	bb, err := syntax.Marshal(p)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(bb))
}
Output:

name = "John Doe"
age  = 43

func MarshalValue

func MarshalValue(v interface{}) ([]byte, error)

MarshalValue returns the pretty-printed encoding of v as an Alloy value.

MarshalValue traverses the value v recursively. If an encountered value implements the encoding.TextMarshaler interface, MarshalValue calls its MarshalText method and encodes the result as an Alloy string. If a value implements the Capsule interface, it always encodes as an Alloy capsule value.

Otherwise, MarshalValue uses the following type-dependent default encodings:

Boolean values encode to Alloy bools.

Floating point, integer, and Number values encode to Alloy numbers.

String values encode to Alloy strings.

Array and slice values encode to Alloy arrays, except that []byte is converted into a Alloy string. Nil slices encode as an empty array and nil []byte slices encode as an empty string.

Structs encode to Alloy objects, using Go struct field tags to determine the resulting structure of the Alloy object. Each exported struct field with an alloy tag becomes an object field, using the tag name as the field name. Other struct fields are ignored. If no struct field has an alloy tag, the struct encodes to an Alloy capsule instead.

Function values encode to Alloy functions, which appear in the resulting text as strings formatted as "function(GO_TYPE)".

All other Go values encode to Alloy capsules, which appear in the resulting text as strings formatted as "capsule(GO_TYPE)".

The alloy tag specifies the field name, possibly followed by a comma-separated list of options. The following provides examples for all supported struct field tags with their meanings:

// Field appears as an object field named "my_name". It will always
// appear in the resulting encoding. When decoding, "my_name" is treated
// as a required attribute and must be present in the source text.
Field bool `alloy:"my_name,attr"`

// Field appears as an object field named "my_name". If the field's value
// is the Go zero value, "example" is omitted from the resulting encoding.
// When decoding, "my_name" is treated as an optional attribute and can be
// omitted from the source text.
Field bool `alloy:"my_name,attr,optional"`
Example
package main

import (
	"fmt"

	"github.com/grafana/alloy/syntax"
)

func main() {
	type Person struct {
		Name string `alloy:"name,attr"`
		Age  int    `alloy:"age,attr"`
	}

	p := Person{
		Name: "John Doe",
		Age:  43,
	}

	bb, err := syntax.MarshalValue(p)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(bb))
}
Output:

{
	name = "John Doe",
	age  = 43,
}

func Unmarshal

func Unmarshal(in []byte, v interface{}) error

Unmarshal converts the Alloy configuration file specified by in and stores it in the struct value pointed to by v. If v is nil or not a pointer, Unmarshal panics. The configuration specified by in may use expressions to compute values while unmarshaling. Refer to the Alloy syntax documentation for the list of valid formatting and expression rules.

Unmarshal uses the inverse of the encoding rules that Marshal uses, allocating maps, slices, and pointers as necessary.

To unmarshal an Alloy body into a map[string]T, Unmarshal assigns each attribute to a key in the map, and decodes the attribute's value as the value for the map entry. Only attribute statements are allowed when unmarshaling into a map.

To unmarshal an Alloy body into a struct, Unmarshal matches incoming attributes and blocks to the alloy struct tags specified by v. Incoming attribute and blocks which do not match to an alloy struct tag cause a decoding error. Additionally, any attribute or block marked as required by the alloy struct tag that are not present in the source text will generate a decoding error.

To unmarshal a list of Alloy blocks into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice.

To unmarshal a list of Alloy blocks into a Go array, Unmarshal decodes each block into the corresponding Go array element. If the number of Alloy blocks does not match the length of the Go array, a decoding error is returned.

Unmarshal follows the rules specified by UnmarshalValue when unmarshaling the value of an attribute.

Example
package main

import (
	"fmt"

	"github.com/grafana/alloy/syntax"
)

func main() {
	// Character is our block type which holds an individual character from a
	// book.
	type Character struct {
		// Name of the character. The name is decoded from the block label.
		Name string `alloy:",label"`
		// Age of the character. The age is a required attribute within the block,
		// and must be set in the config.
		Age int `alloy:"age,attr"`
		// Location the character lives in. The location is an optional attribute
		// within the block. Optional attributes do not have to bet set.
		Location string `alloy:"location,attr,optional"`
	}

	// Book is our overall type where we decode the overall Alloy file into.
	type Book struct {
		// Title of the book (required attribute).
		Title string `alloy:"title,attr"`
		// List of characters. Each character is a labeled block. The optional tag
		// means that it is valid not provide a character block. Decoding into a
		// slice permits there to be multiple specified character blocks.
		Characters []*Character `alloy:"character,block,optional"`
	}

	// Create our book with two characters.
	input := `
		title = "Wheel of Time"

		character "Rand" {
			age      = 19
			location = "Two Rivers"
		}

		character "Perrin" {
			age      = 19
			location = "Two Rivers"
		}
	`

	// Unmarshal the config into our Book type and print out the data.
	var b Book
	if err := syntax.Unmarshal([]byte(input), &b); err != nil {
		panic(err)
	}

	fmt.Printf("%s characters:\n", b.Title)

	for _, c := range b.Characters {
		if c.Location != "" {
			fmt.Printf("\t%s (age %d, location %s)\n", c.Name, c.Age, c.Location)
		} else {
			fmt.Printf("\t%s (age %d)\n", c.Name, c.Age)
		}
	}

}
Output:

Wheel of Time characters:
	Rand (age 19, location Two Rivers)
	Perrin (age 19, location Two Rivers)
Example (Functions)

This example shows how functions may be called within user configurations. We focus on the `env` function from the standard library, which retrieves a value from an environment variable.

package main

import (
	"fmt"
	"os"

	"github.com/grafana/alloy/syntax"
)

func main() {
	// Set an environment variable to use in the test.
	_ = os.Setenv("EXAMPLE", "Jane Doe")

	type Data struct {
		String string `alloy:"string,attr"`
	}

	input := `
		string = env("EXAMPLE")
	`

	var d Data
	if err := syntax.Unmarshal([]byte(input), &d); err != nil {
		panic(err)
	}

	fmt.Println(d.String)
}
Output:

Jane Doe

func UnmarshalValue

func UnmarshalValue(in []byte, v interface{}) error

UnmarshalValue converts the Alloy configuration file specified by in and stores it in the value pointed to by v. If v is nil or not a pointer, UnmarshalValue panics. The configuration specified by in may use expressions to compute values while unmarshaling. Refer to the Alloy syntax documentation for the list of valid formatting and expression rules.

Unmarshal uses the inverse of the encoding rules that MarshalValue uses, allocating maps, slices, and pointers as necessary, with the following additional rules:

After converting an Alloy value into its Go value counterpart, the Go value may be converted into a capsule if the capsule type implements ConvertibleIntoCapsule.

To unmarshal an Alloy object into a struct, UnmarshalValue matches incoming object fields to the alloy struct tags specified by v. Incoming object fields which do not match to an alloy struct tag cause a decoding error. Additionally, any object field marked as required by the alloy struct tag that are not present in the source text will generate a decoding error.

To unmarshal Alloy into an interface value, Unmarshal stores one of the following:

  • bool, for Alloy bools
  • float64, for floating point Alloy numbers and integers which are too big to fit in either of int/int64/uint64
  • int/int64/uint64, in this order of preference, for signed and unsigned Alloy integer numbers, depending on how big they are
  • string, for Alloy strings
  • []interface{}, for Alloy arrays
  • map[string]interface{}, for Alloy objects

Capsule and function types will retain their original type when decoding into an interface value.

To unmarshal an Alloy array into a slice, Unmarshal resets the slice length to zero and then appends each element to the slice.

To unmarshal an Alloy array into a Go array, Unmarshal decodes Alloy array elements into the corresponding Go array element. If the number of elements does not match the length of the Go array, a decoding error is returned.

To unmarshal an Alloy object into a Map, Unmarshal establishes a map to use. If the map is nil, Unmarshal allocates a new map. Otherwise, Unmarshal reuses the existing map, keeping existing entries. Unmarshal then stores key-value pairs from the Alloy object into the map. The map's key type must be string.

Example
package main

import (
	"fmt"

	"github.com/grafana/alloy/syntax"
)

func main() {
	input := `3 + 5`

	var num int
	if err := syntax.UnmarshalValue([]byte(input), &num); err != nil {
		panic(err)
	}

	fmt.Println(num)
}
Output:

8

Types

type Capsule

type Capsule interface {
	// AlloyCapsule marks the type as a Capsule. AlloyCapsule is never invoked by
	// Alloy.
	AlloyCapsule()
}

Capsule is an interface marker which tells Alloy syntax that a type should always be treated as a "capsule type" instead of the default type decoding would assign.

Capsule types are useful for passing around arbitrary Go values in Alloy expressions and for declaring new synthetic types with custom conversion rules.

By default, only two capsule values of the same underlying Go type are compatible. Types which implement ConvertibleFromCapsule or ConvertibleToCapsule can provide custom logic for conversions from and to other types.

type ConvertibleFromCapsule

type ConvertibleFromCapsule interface {
	Capsule

	// ConvertFrom updates the ConvertibleFromCapsule value based on the value of
	// src. src may be any Go value, not just other capsules.
	//
	// ConvertFrom should return ErrNoConversion if no conversion is available
	// from src. Other errors are treated as an Alloy decoding error.
	ConvertFrom(src interface{}) error
}

ConvertibleFromCapsule is a Capsule which supports custom conversion from any Go type which is not the same as the capsule type.

type ConvertibleIntoCapsule

type ConvertibleIntoCapsule interface {
	Capsule

	// ConvertInto should convert its value and store it into dst. dst will be a
	// pointer to a Go value of any type.
	//
	// ConvertInto should return ErrNoConversion if no conversion into dst is
	// available. Other errors are treated as an Alloy decoding error.
	ConvertInto(dst interface{}) error
}

ConvertibleIntoCapsule is a Capsule which supports custom conversion into any Go type which is not the same as the capsule type.

type Decoder

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

Decoder reads Alloy configuration from an input stream. Call NewDecoder to create instances of Decoder.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a new Decoder which reads configuration from r.

func (*Decoder) Decode

func (dec *Decoder) Decode(v interface{}) error

Decode reads the Alloy-encoded file from the Decoder's input and stores it in the value pointed to by v. Data will be read from the Decoder's input until EOF is reached.

See the documentation for Unmarshal for details about the conversion of Alloy configuration into Go values.

func (*Decoder) DecodeValue

func (dec *Decoder) DecodeValue(v interface{}) error

DecodeValue reads the Alloy-encoded expression from the Decoder's input and stores it in the value pointed to by v. Data will be read from the Decoder's input until EOF is reached.

See the documentation for UnmarshalValue for details about the conversion of Alloy values into Go values.

type Defaulter

type Defaulter interface {
	// SetToDefault is called when evaluating a block or body to set the value
	// to its defaults. SetToDefault will not be called on types which are
	// squashed into the parent struct using `alloy:",squash"`.
	SetToDefault()
}

The Defaulter interface allows a type to implement default functionality in Alloy configuration evaluation.

type Encoder

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

Encoder writes Alloy configuration to an output stream. Call NewEncoder to create instances of Encoder.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder returns a new Encoder which writes configuration to w.

func (*Encoder) Encode

func (enc *Encoder) Encode(v interface{}) error

Encode converts the value pointed to by v into an Alloy configuration file and writes the result to the Decoder's output stream.

See the documentation for Marshal for details about the conversion of Go values into Alloy configuration.

func (*Encoder) EncodeValue

func (enc *Encoder) EncodeValue(v interface{}) error

EncodeValue converts the value pointed to by v into an Alloy value and writes the result to the Decoder's output stream.

See the documentation for MarshalValue for details about the conversion of Go values into Alloy values.

type Unmarshaler

type Unmarshaler interface {
	// UnmarshalAlloy is invoked when decoding a Alloy value into a Go value. f
	// should be called with a pointer to a value to decode into. UnmarshalAlloy
	// will not be called on types which are squashed into the parent struct
	// using `alloy:",squash"`.
	UnmarshalAlloy(f func(v interface{}) error) error
}

The Unmarshaler interface allows a type to hook into Alloy syntax decoding and decode into another type or provide pre-decoding logic.

type Validator

type Validator interface {
	// Validate is called when evaluating a block or body to enforce the
	// value is valid. Validate will not be called on types which are
	// squashed into the parent struct using `alloy:",squash"`.
	Validate() error
}

The Validator interface allows a type to implement validation functionality in Alloy configuration evaluation.

Directories

Path Synopsis
Package ast exposes AST elements used by Alloy syntax.
Package ast exposes AST elements used by Alloy syntax.
Package diag exposes error types used throughout Alloy and a method to pretty-print them to the screen.
Package diag exposes error types used throughout Alloy and a method to pretty-print them to the screen.
encoding
alloyjson
Package alloyjson encodes Alloy configuration syntax as JSON.
Package alloyjson encodes Alloy configuration syntax as JSON.
internal
stdlib
Package stdlib contains standard library functions exposed to Alloy configs.
Package stdlib contains standard library functions exposed to Alloy configs.
syntaxtags
Package syntaxtags decodes a struct type into syntax object and structural tags.
Package syntaxtags decodes a struct type into syntax object and structural tags.
value
Package value holds the internal representation for Alloy values.
Package value holds the internal representation for Alloy values.
Package parser implements utilities for parsing Alloy configuration files.
Package parser implements utilities for parsing Alloy configuration files.
Package printer contains utilities for pretty-printing Alloy ASTs.
Package printer contains utilities for pretty-printing Alloy ASTs.
Package scanner implements a lexical scanner for Alloy source files.
Package scanner implements a lexical scanner for Alloy source files.
Package token defines the lexical elements of an Alloy config and utilities surrounding their position.
Package token defines the lexical elements of an Alloy config and utilities surrounding their position.
builder
Package builder exposes an API to create an Alloy configuration file by constructing a set of tokens.
Package builder exposes an API to create an Alloy configuration file by constructing a set of tokens.
Package vm provides an Alloy syntax expression evaluator.
Package vm provides an Alloy syntax expression evaluator.

Jump to

Keyboard shortcuts

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