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

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package hcl2template
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/hashicorp/hcl/v2/hclsyntax"
    11  	packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
    12  	hcl2shim "github.com/hashicorp/packer/hcl2template/shim"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  // DatasourceBlock references an HCL 'data' block.
    17  type DatasourceBlock struct {
    18  	Type         string
    19  	DSName       string
    20  	Dependencies []refString
    21  
    22  	value cty.Value
    23  	block *hcl.Block
    24  }
    25  
    26  type DatasourceRef struct {
    27  	Type string
    28  	Name string
    29  }
    30  
    31  type Datasources map[DatasourceRef]DatasourceBlock
    32  
    33  func (data DatasourceBlock) Name() string {
    34  	return fmt.Sprintf("%s.%s", data.Type, data.DSName)
    35  }
    36  
    37  func (data *DatasourceBlock) Ref() DatasourceRef {
    38  	return DatasourceRef{
    39  		Type: data.Type,
    40  		Name: data.DSName,
    41  	}
    42  }
    43  
    44  func (ds *Datasources) Values() (map[string]cty.Value, hcl.Diagnostics) {
    45  	var diags hcl.Diagnostics
    46  	res := map[string]cty.Value{}
    47  	valuesMap := map[string]map[string]cty.Value{}
    48  
    49  	for ref, datasource := range *ds {
    50  		if datasource.value == (cty.Value{}) {
    51  			diags = append(diags, &hcl.Diagnostic{
    52  				Summary:  "empty value",
    53  				Subject:  &datasource.block.DefRange,
    54  				Severity: hcl.DiagError,
    55  			})
    56  			continue
    57  		}
    58  		inner := valuesMap[ref.Type]
    59  		if inner == nil {
    60  			inner = map[string]cty.Value{}
    61  		}
    62  		inner[ref.Name] = datasource.value
    63  		res[ref.Type] = cty.MapVal(inner)
    64  
    65  		// Keeps values of different datasources from same type
    66  		valuesMap[ref.Type] = inner
    67  	}
    68  
    69  	return res, diags
    70  }
    71  
    72  func (cfg *PackerConfig) startDatasource(ds DatasourceBlock) (packersdk.Datasource, hcl.Diagnostics) {
    73  	var diags hcl.Diagnostics
    74  	block := ds.block
    75  
    76  	dataSourceStore := cfg.parser.PluginConfig.DataSources
    77  
    78  	if dataSourceStore == nil {
    79  		diags = append(diags, &hcl.Diagnostic{
    80  			Summary:  "Unknown " + dataSourceLabel + " type " + ds.Type,
    81  			Subject:  block.LabelRanges[0].Ptr(),
    82  			Detail:   "packer does not currently know any data source.",
    83  			Severity: hcl.DiagError,
    84  		})
    85  		return nil, diags
    86  	}
    87  
    88  	if !dataSourceStore.Has(ds.Type) {
    89  		diags = append(diags, &hcl.Diagnostic{
    90  			Summary:  "Unknown " + dataSourceLabel + " type " + ds.Type,
    91  			Subject:  block.LabelRanges[0].Ptr(),
    92  			Detail:   fmt.Sprintf("known data sources: %v", dataSourceStore.List()),
    93  			Severity: hcl.DiagError,
    94  		})
    95  		return nil, diags
    96  	}
    97  
    98  	datasource, err := dataSourceStore.Start(ds.Type)
    99  	if err != nil {
   100  		diags = append(diags, &hcl.Diagnostic{
   101  			Summary:  err.Error(),
   102  			Subject:  &block.DefRange,
   103  			Severity: hcl.DiagError,
   104  		})
   105  	}
   106  	if datasource == nil {
   107  		diags = append(diags, &hcl.Diagnostic{
   108  			Summary:  fmt.Sprintf("failed to start datasource plugin %q", ds.Name()),
   109  			Subject:  &block.DefRange,
   110  			Severity: hcl.DiagError,
   111  		})
   112  	}
   113  
   114  	var decoded cty.Value
   115  	var moreDiags hcl.Diagnostics
   116  	body := block.Body
   117  	decoded, moreDiags = decodeHCL2Spec(body, cfg.EvalContext(DatasourceContext, nil), datasource)
   118  
   119  	diags = append(diags, moreDiags...)
   120  	if moreDiags.HasErrors() {
   121  		return nil, diags
   122  	}
   123  
   124  	// In case of cty.Unknown values, this will write a equivalent placeholder
   125  	// of the same type. Unknown types are not recognized by the json marshal
   126  	// during the RPC call and we have to do this here to avoid json parsing
   127  	// failures when running the validate command. We don't do this before so
   128  	// we can validate if variable type matches correctly on decodeHCL2Spec.
   129  	decoded = hcl2shim.WriteUnknownPlaceholderValues(decoded)
   130  	if err := datasource.Configure(decoded); err != nil {
   131  		diags = append(diags, &hcl.Diagnostic{
   132  			Summary:  err.Error(),
   133  			Subject:  &block.DefRange,
   134  			Severity: hcl.DiagError,
   135  		})
   136  	}
   137  	return datasource, diags
   138  }
   139  
   140  func (p *Parser) decodeDataBlock(block *hcl.Block) (*DatasourceBlock, hcl.Diagnostics) {
   141  	var diags hcl.Diagnostics
   142  	r := &DatasourceBlock{
   143  		Type:   block.Labels[0],
   144  		DSName: block.Labels[1],
   145  		block:  block,
   146  	}
   147  
   148  	if !hclsyntax.ValidIdentifier(r.Type) {
   149  		diags = append(diags, &hcl.Diagnostic{
   150  			Severity: hcl.DiagError,
   151  			Summary:  "Invalid data source name",
   152  			Detail:   badIdentifierDetail,
   153  			Subject:  &block.LabelRanges[0],
   154  		})
   155  	}
   156  	if !hclsyntax.ValidIdentifier(r.DSName) {
   157  		diags = append(diags, &hcl.Diagnostic{
   158  			Severity: hcl.DiagError,
   159  			Summary:  "Invalid data resource name",
   160  			Detail:   badIdentifierDetail,
   161  			Subject:  &block.LabelRanges[1],
   162  		})
   163  	}
   164  
   165  	return r, diags
   166  }