github.com/hashicorp/packer@v1.14.3/hcl2template/types.refstring.go (about)

     1  package hcl2template
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  )
     9  
    10  // A refstring is any reference string that can point to a component of a config
    11  //
    12  // This includes anything in the following format:
    13  //
    14  // * data.<type>.<name>
    15  // * var.<name>
    16  // * local.<name>
    17  type refString struct {
    18  	// RefMType is the top-level label, that is used to know which type of
    19  	// component we are to look for (i.e. data for Datasource, local for
    20  	// Locals, etc.)
    21  	MType string
    22  	// RefType is the type of component, as in the name of the plugin that
    23  	// will handle evaluation for the component.
    24  	//
    25  	// Only for Datasources now as var/local do not have a component type
    26  	// (they're always evaluated by Packer itself, not a plugin).
    27  	Type string
    28  	// RefName is the name of the component to get.
    29  	// For locals/vars this is the name of the variable to look for, while
    30  	// for datasources this is the name of the block, which coupled with the
    31  	// type is the identity of the datasource's execution.
    32  	Name string
    33  }
    34  
    35  func NewRefStringFromDep(t hcl.Traversal) (refString, error) {
    36  	root := t.RootName()
    37  
    38  	switch root {
    39  	case "local", "var":
    40  		return NewRefString(fmt.Sprintf("%s.%s", root, t[1].(hcl.TraverseAttr).Name))
    41  	case "data":
    42  		return NewRefString(fmt.Sprintf("%s.%s.%s", root,
    43  			t[1].(hcl.TraverseAttr).Name,
    44  			t[2].(hcl.TraverseAttr).Name))
    45  	}
    46  
    47  	return refString{}, fmt.Errorf("unsupported refstring %q, must be of 'data', 'local' or 'var' type", t)
    48  }
    49  
    50  func NewRefString(rs string) (refString, error) {
    51  	parts := strings.Split(rs, ".")
    52  
    53  	switch parts[0] {
    54  	case "local", "var":
    55  		return refString{
    56  			MType: parts[0],
    57  			Name:  parts[1],
    58  		}, nil
    59  	case "data":
    60  		return newDataSourceRefString(parts)
    61  	}
    62  
    63  	return refString{}, fmt.Errorf("unsupported reftype %q, must be either 'data', 'local' or 'var'", parts[0])
    64  }
    65  
    66  func (rs refString) String() string {
    67  	if rs.Type == "" {
    68  		return fmt.Sprintf("%s.%s", rs.MType, rs.Name)
    69  	}
    70  
    71  	return fmt.Sprintf("%s.%s.%s", rs.MType, rs.Type, rs.Name)
    72  }
    73  
    74  func newDataSourceRefString(parts []string) (refString, error) {
    75  	if len(parts) != 3 {
    76  		return refString{}, fmt.Errorf("malformed datasource reference %q, data sources must be composed of 3 parts",
    77  			strings.Join(parts, "."))
    78  	}
    79  
    80  	return refString{
    81  		MType: "data",
    82  		Type:  parts[1],
    83  		Name:  parts[2],
    84  	}, nil
    85  }
    86  
    87  // getComponentByRef gets a registered component from the configuration from a refString
    88  func (cfg *PackerConfig) getComponentByRef(rs refString) (interface{}, error) {
    89  	switch rs.MType {
    90  	case "data":
    91  		for _, ds := range cfg.Datasources {
    92  			if ds.Type != rs.Type {
    93  				continue
    94  			}
    95  			if ds.DSName != rs.Name {
    96  				continue
    97  			}
    98  			return ds, nil
    99  		}
   100  		return nil, fmt.Errorf("failed to get datasource '%s.%s': component unknown", rs.Type, rs.Name)
   101  	case "local":
   102  		for _, loc := range cfg.LocalBlocks {
   103  			if loc.LocalName != rs.Name {
   104  				continue
   105  			}
   106  			return loc, nil
   107  		}
   108  	case "var":
   109  		for _, val := range cfg.InputVariables {
   110  			if val.Name != rs.Name {
   111  				continue
   112  			}
   113  			return val, nil
   114  		}
   115  	}
   116  
   117  	return nil, fmt.Errorf("Unsupported component: %q, only vars, locals and datasources can be fetched by their refString", rs)
   118  }
   119  
   120  func (ds *DatasourceBlock) RegisterDependency(rs refString) error {
   121  	switch rs.MType {
   122  	case "data", "local":
   123  		ds.Dependencies = append(ds.Dependencies, rs)
   124  	// NOOP: vars are always evaluated beforehand for datasources
   125  	case "var":
   126  	default:
   127  		return fmt.Errorf("unsupported dependency type %q; datasources can only depend on local, var or data.", rs.MType)
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  func (loc *LocalBlock) RegisterDependency(rs refString) error {
   134  	switch rs.MType {
   135  	case "data", "local":
   136  		loc.dependencies = append(loc.dependencies, rs)
   137  	// NOOP: vars are always evaluated beforehand for locals
   138  	case "var":
   139  	default:
   140  		return fmt.Errorf("unsupported dependency type %q; locals can only depend on local, var or data.", rs.MType)
   141  	}
   142  
   143  	return nil
   144  }