packer/hcl2template/types.refstring.go
Lucas Bajolet 4a4b837386 hcl2template: rename Name->LocalName for local
Local variables had an attribute called Name with the name of the local
variable.

However, when producing an error while walking the DAG of
local/datasources, if an error is encountered during validation, the raw
structure of the vertex was printed out, making the error message
produced hard to understand.

Therefore in order to clean it up, we rename the `Name` attribute for
Local variables as `LocalName`, and introduce a `Name()` function for
that block so that the complete name of the variable is clearly
reported.
2024-10-29 16:10:29 -04:00

144 lines
3.8 KiB
Go

package hcl2template
import (
"fmt"
"strings"
"github.com/hashicorp/hcl/v2"
)
// A refstring is any reference string that can point to a component of a config
//
// This includes anything in the following format:
//
// * data.<type>.<name>
// * var.<name>
// * local.<name>
type refString struct {
// RefMType is the top-level label, that is used to know which type of
// component we are to look for (i.e. data for Datasource, local for
// Locals, etc.)
MType string
// RefType is the type of component, as in the name of the plugin that
// will handle evaluation for the component.
//
// Only for Datasources now as var/local do not have a component type
// (they're always evaluated by Packer itself, not a plugin).
Type string
// RefName is the name of the component to get.
// For locals/vars this is the name of the variable to look for, while
// for datasources this is the name of the block, which coupled with the
// type is the identity of the datasource's execution.
Name string
}
func NewRefStringFromDep(t hcl.Traversal) (refString, error) {
root := t.RootName()
switch root {
case "local", "var":
return NewRefString(fmt.Sprintf("%s.%s", root, t[1].(hcl.TraverseAttr).Name))
case "data":
return NewRefString(fmt.Sprintf("%s.%s.%s", root,
t[1].(hcl.TraverseAttr).Name,
t[2].(hcl.TraverseAttr).Name))
}
return refString{}, fmt.Errorf("unsupported refstring %q, must be of 'data', 'local' or 'var' type", t)
}
func NewRefString(rs string) (refString, error) {
parts := strings.Split(rs, ".")
switch parts[0] {
case "local", "var":
return refString{
MType: parts[0],
Name: parts[1],
}, nil
case "data":
return newDataSourceRefString(parts)
}
return refString{}, fmt.Errorf("unsupported reftype %q, must be either 'data', 'local' or 'var'", parts[0])
}
func (rs refString) String() string {
if rs.Type == "" {
return fmt.Sprintf("%s.%s", rs.MType, rs.Name)
}
return fmt.Sprintf("%s.%s.%s", rs.MType, rs.Type, rs.Name)
}
func newDataSourceRefString(parts []string) (refString, error) {
if len(parts) != 3 {
return refString{}, fmt.Errorf("malformed datasource reference %q, data sources must be composed of 3 parts",
strings.Join(parts, "."))
}
return refString{
MType: "data",
Type: parts[1],
Name: parts[2],
}, nil
}
// getComponentByRef gets a registered component from the configuration from a refString
func (cfg *PackerConfig) getComponentByRef(rs refString) (interface{}, error) {
switch rs.MType {
case "data":
for _, ds := range cfg.Datasources {
if ds.Type != rs.Type {
continue
}
if ds.DSName != rs.Name {
continue
}
return ds, nil
}
return nil, fmt.Errorf("failed to get datasource '%s.%s': component unknown", rs.Type, rs.Name)
case "local":
for _, loc := range cfg.LocalBlocks {
if loc.LocalName != rs.Name {
continue
}
return loc, nil
}
case "var":
for _, val := range cfg.InputVariables {
if val.Name != rs.Name {
continue
}
return val, nil
}
}
return nil, fmt.Errorf("Unsupported component: %q, only vars, locals and datasources can be fetched by their refString", rs)
}
func (ds *DatasourceBlock) RegisterDependency(rs refString) error {
switch rs.MType {
case "data", "local":
ds.Dependencies = append(ds.Dependencies, rs)
// NOOP: vars are always evaluated beforehand for datasources
case "var":
default:
return fmt.Errorf("unsupported dependency type %q; datasources can only depend on local, var or data.", rs.MType)
}
return nil
}
func (loc *LocalBlock) RegisterDependency(rs refString) error {
switch rs.MType {
case "data", "local":
loc.dependencies = append(loc.dependencies, rs)
// NOOP: vars are always evaluated beforehand for locals
case "var":
default:
return fmt.Errorf("unsupported dependency type %q; locals can only depend on local, var or data.", rs.MType)
}
return nil
}