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 }