You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
packer/hcl2template/types.refstring.go

145 lines
3.8 KiB

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
}