Skip to content

Commit

Permalink
feat: Add support for module metadata arguments (#186)
Browse files Browse the repository at this point in the history
* feat: Add support for module metadata arguments

- Support providers block
- Support for_each block

Add sample workaround for default_tags provider issue

* feat: Add integration registry support for for_each modules
  • Loading branch information
vincenthsh authored Sep 14, 2023
1 parent 4b0d1a0 commit 7ed4cee
Show file tree
Hide file tree
Showing 21 changed files with 3,963 additions and 454 deletions.
146 changes: 90 additions & 56 deletions apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,11 +395,13 @@ func applyEnvs(
}
mi = append(mi, moduleInvocation{
module: v2.ComponentModule{
Name: componentPlan.ModuleName,
Source: componentPlan.ModuleSource,
Version: nil,
Variables: componentPlan.Variables,
Prefix: nil,
Name: componentPlan.ModuleName,
Source: componentPlan.ModuleSource,
ForEach: componentPlan.ModuleForEach,
Version: nil,
Variables: componentPlan.Variables,
Prefix: nil,
ProvidersMap: componentPlan.ProvidersMap,
},
downloadFunc: downloader,
})
Expand Down Expand Up @@ -586,9 +588,11 @@ type moduleData struct {
ModuleSource string
ModuleVersion string
ModulePrefix string
ModuleForEach *string
Variables []string
Outputs []*tfconfig.Output
IntegrationRegistryEntries []*IntegrationRegistryEntry
ProvidersMap map[string]string
}

type IntegrationRegistryEntry struct {
Expand All @@ -600,6 +604,7 @@ type IntegrationRegistryEntry struct {
Path *string
ForEach bool
PathForEach *string
Provider *string
}

type modulesData struct {
Expand Down Expand Up @@ -661,63 +666,14 @@ func applyModuleInvocation(
integrationRegistryEntries := make([]*IntegrationRegistryEntry, 0)
integration := mi.module.Integration
if integration == nil {
// default to "none"
integration = &v2.ModuleIntegrationConfig{
Mode: util.StrPtr("none"),
}
}
for _, o := range moduleConfig.Outputs {
outputs = append(outputs, o)
outputRef := fmt.Sprintf("module.%s.%s", moduleName, o.Name)
outputRefFmted := outputRef
if integration.Format != nil {
outputRefFmted = fmt.Sprintf(*integration.Format, outputRef)
}
if *integration.Mode != "none" {
wrapEntry := IntegrationRegistryEntry{
Output: o,
OutputRef: outputRefFmted,
DropComponent: integration.DropComponent,
DropPrefix: integration.DropPrefix,
PathInfix: integration.PathInfix,
}
for eName, e := range integration.OutputsMap {
if o.Name == eName {
if e.Format != nil {
outputRefFmted = fmt.Sprintf(*e.Format, outputRef)
}
if e.ForEach {
outputRef = "each.value"
outputRefFmted = outputRef
if integration.Format != nil {
outputRefFmted = fmt.Sprintf(*integration.Format, outputRef)
}
if e.Format != nil {
outputRefFmted = fmt.Sprintf(*e.Format, outputRef)
}
}
dropComponent := integration.DropComponent
if e.DropComponent != nil {
dropComponent = *e.DropComponent
}
wrapEntry = IntegrationRegistryEntry{
Output: o,
OutputRef: outputRefFmted,
DropComponent: dropComponent,
DropPrefix: integration.DropPrefix,
PathInfix: integration.PathInfix,
Path: e.Path,
ForEach: e.ForEach,
PathForEach: e.PathForEach,
}
if *integration.Mode == "selected" {
integrationRegistryEntries = append(integrationRegistryEntries, &wrapEntry)
}
}
}
if *integration.Mode != "selected" {
integrationRegistryEntries = append(integrationRegistryEntries, &wrapEntry)
}
}
integrationRegistryEntries = integrateOutput(moduleName, o, mi, integration, integrationRegistryEntries)
}
sort.Slice(outputs, func(i, j int) bool {
return outputs[i].Name < outputs[j].Name
Expand All @@ -740,9 +696,11 @@ func applyModuleInvocation(
ModuleSource: moduleAddressForSource,
ModuleVersion: moduleVersion,
ModulePrefix: modulePrefix,
ModuleForEach: mi.module.ForEach,
Variables: variables,
Outputs: outputs,
IntegrationRegistryEntries: integrationRegistryEntries,
ProvidersMap: mi.module.ProvidersMap,
})
}

Expand Down Expand Up @@ -802,6 +760,82 @@ func applyModuleInvocation(
return nil
}

// Evaluate integrationRegistry configuration and return updated list of integrationRegistryEntries
func integrateOutput(
moduleName string,
o *tfconfig.Output,
mi moduleInvocation,
integration *v2.ModuleIntegrationConfig,
integrationRegistryEntries []*IntegrationRegistryEntry,
) []*IntegrationRegistryEntry {
// dont integrate
if *integration.Mode == "none" {
return integrationRegistryEntries
}

outputRef := fmt.Sprintf("module.%s.%s", moduleName, o.Name)
if mi.module.ForEach != nil {
outputRef = "each.value"
}
outputRefFmted := outputRef
if integration.Format != nil {
outputRefFmted = fmt.Sprintf(*integration.Format, outputRef)
}
wrapEntry := IntegrationRegistryEntry{
Output: o,
OutputRef: outputRefFmted,
DropComponent: integration.DropComponent,
DropPrefix: integration.DropPrefix,
PathInfix: integration.PathInfix,
Provider: integration.Provider,
}
for eName, e := range integration.OutputsMap {
if o.Name == eName {
if e.Format != nil {
outputRefFmted = fmt.Sprintf(*e.Format, outputRef)
}
if e.ForEach {
if mi.module.ForEach != nil {
outputRef = "each.value.output_value"
} else {
outputRef = "each.value"
}
outputRefFmted = outputRef
if integration.Format != nil {
outputRefFmted = fmt.Sprintf(*integration.Format, outputRef)
}
if e.Format != nil {
outputRefFmted = fmt.Sprintf(*e.Format, outputRef)
}
}
dropComponent := integration.DropComponent
if e.DropComponent != nil {
dropComponent = *e.DropComponent
}
wrapEntry = IntegrationRegistryEntry{
Output: o,
OutputRef: outputRefFmted,
DropComponent: dropComponent,
DropPrefix: integration.DropPrefix,
PathInfix: integration.PathInfix,
Path: e.Path,
ForEach: e.ForEach,
PathForEach: e.PathForEach,
Provider: integration.Provider,
}
// integrate only mapped
if *integration.Mode == "selected" {
integrationRegistryEntries = append(integrationRegistryEntries, &wrapEntry)
}
}
}
// also integrate non-mapped outputs
if *integration.Mode != "selected" {
integrationRegistryEntries = append(integrationRegistryEntries, &wrapEntry)
}
return integrationRegistryEntries
}

func calculateModuleAddressForSource(path, moduleAddress string, moduleVersion string) (string, string, error) {
if moduleVersion != "" && util.IsRegistrySourceAddr(moduleAddress) {
return moduleAddress, moduleVersion, nil
Expand Down
20 changes: 14 additions & 6 deletions config/v2/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ type Env struct {
type Component struct {
Common `yaml:",inline"`

EKS *EKSConfig `yaml:"eks,omitempty"`
Kind *ComponentKind `yaml:"kind,omitempty"`
ModuleSource *string `yaml:"module_source,omitempty"`
ModuleName *string `yaml:"module_name,omitempty"`
Variables []string `yaml:"variables,omitempty"`
Modules []ComponentModule `yaml:"modules,omitempty"`
EKS *EKSConfig `yaml:"eks,omitempty"`
Kind *ComponentKind `yaml:"kind,omitempty"`
ModuleSource *string `yaml:"module_source,omitempty"`
ModuleName *string `yaml:"module_name,omitempty"`
ModuleForEach *string `yaml:"module_for_each,omitempty"`
ProvidersMap map[string]string `yaml:"module_providers,omitempty"`
Variables []string `yaml:"variables,omitempty"`
Modules []ComponentModule `yaml:"modules,omitempty"`
}

type ComponentModule struct {
Expand All @@ -151,6 +153,10 @@ type ComponentModule struct {
Variables []string `yaml:"variables,omitempty"`
// Integration Registry config
Integration *ModuleIntegrationConfig `yaml:"integration,omitempty"`
// Optional mapping of providers https://developer.hashicorp.com/terraform/language/meta-arguments/module-providers
ProvidersMap map[string]string `yaml:"providers,omitempty"`
// For Each metadata argument https://developer.hashicorp.com/terraform/language/modules/syntax#meta-arguments
ForEach *string `yaml:"for_each,omitempty"`
}

type ModuleIntegrationConfig struct {
Expand All @@ -166,6 +172,8 @@ type ModuleIntegrationConfig struct {
DropComponent bool `yaml:"drop_component,omitempty"`
// Infix path for all outputs
PathInfix *string `yaml:"path_infix,omitempty"`
// Resource provider https://developer.hashicorp.com/terraform/language/meta-arguments/resource-provider
Provider *string `yaml:"provider,omitempty"`
// Map for outputs into Integration Registry
OutputsMap map[string]*IntegrationRegistryMap `yaml:"outputs_map,omitempty"`
}
Expand Down
18 changes: 11 additions & 7 deletions plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,15 @@ type Account struct {
type Component struct {
ComponentCommon `yaml:",inline"`

EKS *v2.EKSConfig `yaml:"eks,omitempty"`
Kind *v2.ComponentKind `yaml:"kind,omitempty"`
ModuleSource *string `yaml:"module_source"`
ModuleName *string `yaml:"module_name"`
Variables []string `yaml:"variables"`
Modules []v2.ComponentModule `yaml:"modules"`
Global *Component `yaml:"global"`
EKS *v2.EKSConfig `yaml:"eks,omitempty"`
Kind *v2.ComponentKind `yaml:"kind,omitempty"`
ModuleSource *string `yaml:"module_source"`
ModuleName *string `yaml:"module_name"`
ModuleForEach *string `yaml:"module_for_each"`
ProvidersMap map[string]string `yaml:"providers"`
Variables []string `yaml:"variables"`
Modules []v2.ComponentModule `yaml:"modules"`
Global *Component `yaml:"global"`
}

// Env is an env
Expand Down Expand Up @@ -625,8 +627,10 @@ func (p *Plan) buildEnvs(conf *v2.Config) (map[string]Env, error) {
componentPlan.Name = componentName
componentPlan.ModuleSource = componentConf.ModuleSource
componentPlan.ModuleName = componentConf.ModuleName
componentPlan.ModuleForEach = componentConf.ModuleForEach
componentPlan.Variables = componentConf.Variables
componentPlan.Modules = componentConf.Modules
componentPlan.ProvidersMap = componentConf.ProvidersMap
componentPlan.PathToRepoRoot = "../../../../"

if !envConf.NoGlobal {
Expand Down
25 changes: 20 additions & 5 deletions templates/templates/module-invocation/main.tf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,28 @@
# Make improvements in fogg, so that everyone can benefit.
{{ range .Modules }}
module "{{.ModuleName}}" {
{{- if .ModuleForEach }}
for_each = {{ .ModuleForEach }}
{{- end }}
source = "{{.ModuleSource}}"
{{ if .ModuleVersion -}}
{{- if .ModuleVersion }}
version = "{{ .ModuleVersion }}"
{{ end -}}
{{ $outer := . -}}
{{range .Variables -}}
{{- end }}
{{- $outer := . }}
{{- range .Variables }}
{{- if $outer.ModuleForEach }}
{{.}} = each.value.{{.}}
{{- else }}
{{.}} = local.{{$outer.ModulePrefix}}{{.}}
{{ end -}}
{{- end }}
{{- end }}
{{- if .ProvidersMap }}

providers = {
{{- range $child_provider, $parent_provider := .ProvidersMap}}
{{ $child_provider }} = {{ $parent_provider }}
{{- end}}
}
{{- end }}
}
{{ end }}
6 changes: 6 additions & 0 deletions templates/templates/module-invocation/outputs.tf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
{{ $outer := . -}}
{{- range .Outputs -}}
output "{{$outer.ModulePrefix}}{{.Name}}" {
{{- if $outer.ModuleForEach }}
value = { for k, v in module.{{$outer.ModuleName}}:
k => v.{{.Name}}
}
{{- else }}
value = module.{{$outer.ModuleName}}.{{.Name}}
{{- end }}
sensitive = {{.Sensitive}}
}
{{end }}
Expand Down
Loading

0 comments on commit 7ed4cee

Please sign in to comment.