mirror of
https://github.com/hashicorp/packer.git
synced 2026-05-23 18:37:00 -04:00
When registering dependencies for datasources and locals, we now use refString. This allows for the functions that detect dependencies to not only be able to register the same types as dependencies, but instead generalises it to any type that refString supports, so data, local and var. This can then be leveraged for orchestrating evaluation of those components in a non-phased way (i.e. with a DAG for dependency management).
166 lines
4.3 KiB
Go
166 lines
4.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package hcl2template
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
hcl2shim "github.com/hashicorp/packer/hcl2template/shim"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// DatasourceBlock references an HCL 'data' block.
|
|
type DatasourceBlock struct {
|
|
Type string
|
|
DSName string
|
|
Dependencies []refString
|
|
|
|
value cty.Value
|
|
block *hcl.Block
|
|
}
|
|
|
|
type DatasourceRef struct {
|
|
Type string
|
|
Name string
|
|
}
|
|
|
|
type Datasources map[DatasourceRef]DatasourceBlock
|
|
|
|
func (data DatasourceBlock) Name() string {
|
|
return fmt.Sprintf("%s.%s", data.Type, data.DSName)
|
|
}
|
|
|
|
func (data *DatasourceBlock) Ref() DatasourceRef {
|
|
return DatasourceRef{
|
|
Type: data.Type,
|
|
Name: data.DSName,
|
|
}
|
|
}
|
|
|
|
func (ds *Datasources) Values() (map[string]cty.Value, hcl.Diagnostics) {
|
|
var diags hcl.Diagnostics
|
|
res := map[string]cty.Value{}
|
|
valuesMap := map[string]map[string]cty.Value{}
|
|
|
|
for ref, datasource := range *ds {
|
|
if datasource.value == (cty.Value{}) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: "empty value",
|
|
Subject: &datasource.block.DefRange,
|
|
Severity: hcl.DiagError,
|
|
})
|
|
continue
|
|
}
|
|
inner := valuesMap[ref.Type]
|
|
if inner == nil {
|
|
inner = map[string]cty.Value{}
|
|
}
|
|
inner[ref.Name] = datasource.value
|
|
res[ref.Type] = cty.MapVal(inner)
|
|
|
|
// Keeps values of different datasources from same type
|
|
valuesMap[ref.Type] = inner
|
|
}
|
|
|
|
return res, diags
|
|
}
|
|
|
|
func (cfg *PackerConfig) startDatasource(ds DatasourceBlock) (packersdk.Datasource, hcl.Diagnostics) {
|
|
var diags hcl.Diagnostics
|
|
block := ds.block
|
|
|
|
dataSourceStore := cfg.parser.PluginConfig.DataSources
|
|
|
|
if dataSourceStore == nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: "Unknown " + dataSourceLabel + " type " + ds.Type,
|
|
Subject: block.LabelRanges[0].Ptr(),
|
|
Detail: "packer does not currently know any data source.",
|
|
Severity: hcl.DiagError,
|
|
})
|
|
return nil, diags
|
|
}
|
|
|
|
if !dataSourceStore.Has(ds.Type) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: "Unknown " + dataSourceLabel + " type " + ds.Type,
|
|
Subject: block.LabelRanges[0].Ptr(),
|
|
Detail: fmt.Sprintf("known data sources: %v", dataSourceStore.List()),
|
|
Severity: hcl.DiagError,
|
|
})
|
|
return nil, diags
|
|
}
|
|
|
|
datasource, err := dataSourceStore.Start(ds.Type)
|
|
if err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: err.Error(),
|
|
Subject: &block.DefRange,
|
|
Severity: hcl.DiagError,
|
|
})
|
|
}
|
|
if datasource == nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: fmt.Sprintf("failed to start datasource plugin %q", ds.Name()),
|
|
Subject: &block.DefRange,
|
|
Severity: hcl.DiagError,
|
|
})
|
|
}
|
|
|
|
var decoded cty.Value
|
|
var moreDiags hcl.Diagnostics
|
|
body := block.Body
|
|
decoded, moreDiags = decodeHCL2Spec(body, cfg.EvalContext(DatasourceContext, nil), datasource)
|
|
|
|
diags = append(diags, moreDiags...)
|
|
if moreDiags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
// In case of cty.Unknown values, this will write a equivalent placeholder
|
|
// of the same type. Unknown types are not recognized by the json marshal
|
|
// during the RPC call and we have to do this here to avoid json parsing
|
|
// failures when running the validate command. We don't do this before so
|
|
// we can validate if variable type matches correctly on decodeHCL2Spec.
|
|
decoded = hcl2shim.WriteUnknownPlaceholderValues(decoded)
|
|
if err := datasource.Configure(decoded); err != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Summary: err.Error(),
|
|
Subject: &block.DefRange,
|
|
Severity: hcl.DiagError,
|
|
})
|
|
}
|
|
return datasource, diags
|
|
}
|
|
|
|
func (p *Parser) decodeDataBlock(block *hcl.Block) (*DatasourceBlock, hcl.Diagnostics) {
|
|
var diags hcl.Diagnostics
|
|
r := &DatasourceBlock{
|
|
Type: block.Labels[0],
|
|
DSName: block.Labels[1],
|
|
block: block,
|
|
}
|
|
|
|
if !hclsyntax.ValidIdentifier(r.Type) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid data source name",
|
|
Detail: badIdentifierDetail,
|
|
Subject: &block.LabelRanges[0],
|
|
})
|
|
}
|
|
if !hclsyntax.ValidIdentifier(r.DSName) {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid data resource name",
|
|
Detail: badIdentifierDetail,
|
|
Subject: &block.LabelRanges[1],
|
|
})
|
|
}
|
|
|
|
return r, diags
|
|
}
|