Skip to content

Commit

Permalink
First implementation of module attribution.
Browse files Browse the repository at this point in the history
A pass at implementing module attribution ("provider_meta blocks in the
terraform block", whatever, it's for module attribution).

The general idea is that we're adding a new ProviderMeta struct to our
configs package, which is then added to Files, Modules, and parsed as
part of the terraform block.

The configs.Config type then makes that available to the
terraform.ProviderTransformer type, which is already scoped by module.
It picks out the ProviderMeta for the provider we need in its Transform
method, and checks to see if the node it's working on implements the
terraform.GraphNodeAttachProviderMetaConfigs interface, which is a new
interface we're adding. It has only one method:
AttachProviderMetaConfigs, which is passed a *configs.ProviderMeta.

We implement this on the terraform.NodeAbstractResource type, storing
the passed *configs.ProviderMeta in a new property on that struct.

That struct is eventually made available to the
terraform.NodeApplyableResourceInstance type, and its method
evalTreeManagedResources, which creates an EvalApply type.

The EvalApply type gets a ProviderMeta type, which is set to the
*configs.ProviderMeta obtained from the terraform.NodeAbstractResource
available to it.

This means that EvalApply now has the raw config values the user entered
for the provider_meta block, but we also need a schema; providers can
define their own schema for the provider_meta block, and we need that
schema to know how to translate what is in the config into a cty.Value,
which is what we eventually want here.

Fortunately, EvalApply already has access to the provider's schema; we
can augment the GetSchema RPC call to pass back the ProviderMeta schema
(added to helper/schema.Provider) in addition to resource, data source,
and provider schemas. This makes it available, eventually, to EvalApply.

Now that EvalApply has both the config values and the schema, we can
parse them into a cty.Value.

Finally, we augment the ApplyResourceChange RPC call with a ProviderMeta
field, which can be populated by EvalApply with the parsed cty.Value,
which successfully makes the config values available to the provider.

This still needs tests written for it to ensure it works, but no current
tests are broken on it and it compiles, which is reassuring.
  • Loading branch information
paddycarver committed Aug 24, 2019
1 parent 10d94fb commit f78d386
Show file tree
Hide file tree
Showing 20 changed files with 830 additions and 449 deletions.
Empty file.
14 changes: 14 additions & 0 deletions configs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Module struct {
Backend *Backend
ProviderConfigs map[string]*Provider
ProviderRequirements map[string][]VersionConstraint
ProviderMetas map[string]*ProviderMeta

Variables map[string]*Variable
Locals map[string]*Local
Expand Down Expand Up @@ -56,6 +57,7 @@ type File struct {
Backends []*Backend
ProviderConfigs []*Provider
ProviderRequirements []*ProviderRequirement
ProviderMetas []*ProviderMeta

Variables []*Variable
Locals []*Local
Expand Down Expand Up @@ -164,6 +166,18 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
m.ProviderRequirements[reqd.Name] = append(m.ProviderRequirements[reqd.Name], reqd.Requirement)
}

for _, pm := range file.ProviderMetas {
if existing, exists := m.ProviderMetas[pm.Provider]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider_meta block",
Detail: fmt.Sprintf("A provider_meta block for provider %q was already declared at %s. Providers may only have one provider_meta block per module.", existing.Provider, existing.DeclRange),
Subject: &pm.DeclRange,
})
}
m.ProviderMetas[pm.Provider] = pm
}

for _, v := range file.Variables {
if existing, exists := m.Variables[v.Name]; exists {
diags = append(diags, &hcl.Diagnostic{
Expand Down
10 changes: 10 additions & 0 deletions configs/parser_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
reqs, reqsDiags := decodeRequiredProvidersBlock(innerBlock)
diags = append(diags, reqsDiags...)
file.ProviderRequirements = append(file.ProviderRequirements, reqs...)
case "provider_meta":
providerCfg, cfgDiags := decodeProviderMetaBlock(innerBlock)
diags = append(diags, cfgDiags...)
if providerCfg != nil {
file.ProviderMetas = append(file.ProviderMetas, providerCfg)
}

default:
// Should never happen because the above cases should be exhaustive
Expand Down Expand Up @@ -225,6 +231,10 @@ var terraformBlockSchema = &hcl.BodySchema{
{
Type: "required_providers",
},
{
Type: "provider_meta",
LabelNames: []string{"provider"},
},
},
}

Expand Down
22 changes: 22 additions & 0 deletions configs/provider_meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package configs

import "github.com/hashicorp/hcl2/hcl"

// ProviderMeta represents a "provider_meta" block inside a "terraform" block
// in a module or file.
type ProviderMeta struct {
Provider string
Config hcl.Body

ProviderRange hcl.Range
DeclRange hcl.Range
}

func decodeProviderMetaBlock(block *hcl.Block) (*ProviderMeta, hcl.Diagnostics) {
return &ProviderMeta{
Provider: block.Labels[0],
ProviderRange: block.LabelRanges[0],
Config: block.Body,
DeclRange: block.DefRange,
}, nil
}
Loading

0 comments on commit f78d386

Please sign in to comment.