Integrate encryption config into configs package (#1295)

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
Christian Mesh 2024-03-04 11:04:45 -05:00 committed by GitHub
parent 2f5dcd5c0a
commit 36eb93f958
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 98 additions and 24 deletions

View File

@ -3,7 +3,7 @@
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configs
package hcl2shim
import (
"github.com/hashicorp/hcl/v2"
@ -46,7 +46,7 @@ var _ hcl.Body = mergeBody{}
func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
var diags hcl.Diagnostics
baseSchema := schemaWithDynamic(schema)
overrideSchema := schemaWithDynamic(schemaForOverrides(schema))
overrideSchema := schemaWithDynamic(SchemaForOverrides(schema))
baseContent, _, cDiags := b.Base.PartialContent(baseSchema)
diags = append(diags, cDiags...)
@ -61,7 +61,7 @@ func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagno
func (b mergeBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
var diags hcl.Diagnostics
baseSchema := schemaWithDynamic(schema)
overrideSchema := schemaWithDynamic(schemaForOverrides(schema))
overrideSchema := schemaWithDynamic(SchemaForOverrides(schema))
baseContent, baseRemain, cDiags := b.Base.PartialContent(baseSchema)
diags = append(diags, cDiags...)

View File

@ -3,7 +3,7 @@
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configs
package hcl2shim
import (
"fmt"

View File

@ -3,7 +3,7 @@
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configs
package hcl2shim
import (
"testing"

View File

@ -3,7 +3,7 @@
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configs
package hcl2shim
import (
"github.com/hashicorp/hcl/v2"
@ -17,7 +17,7 @@ import (
// decoding would've expected a keyword or reference in quotes but our new
// decoding expects the keyword or reference to be provided directly as
// an identifier-based expression.
func exprIsNativeQuotedString(expr hcl.Expression) bool {
func ExprIsNativeQuotedString(expr hcl.Expression) bool {
_, ok := expr.(*hclsyntax.TemplateExpr)
return ok
}
@ -35,7 +35,7 @@ func exprIsNativeQuotedString(expr hcl.Expression) bool {
// Overrides are rarely used, so it's recommended to just create the override
// schema on the fly only when it's needed, rather than storing it in a global
// variable as we tend to do for a primary schema.
func schemaForOverrides(schema *hcl.BodySchema) *hcl.BodySchema {
func SchemaForOverrides(schema *hcl.BodySchema) *hcl.BodySchema {
ret := &hcl.BodySchema{
Attributes: make([]hcl.AttributeSchema, len(schema.Attributes)),
Blocks: schema.Blocks,

View File

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/encryption/config"
"github.com/opentofu/opentofu/internal/experiments"
tfversion "github.com/opentofu/opentofu/version"
@ -41,6 +42,7 @@ type Module struct {
ProviderRequirements *RequiredProviders
ProviderLocalNames map[addrs.Provider]string
ProviderMetas map[addrs.Provider]*ProviderMeta
Encryption *config.EncryptionConfig
Variables map[string]*Variable
Locals map[string]*Local
@ -81,6 +83,7 @@ type File struct {
ProviderConfigs []*Provider
ProviderMetas []*ProviderMeta
RequiredProviders []*RequiredProviders
Encryptions []*config.EncryptionConfig
Variables []*Variable
Locals []*Local
@ -280,6 +283,19 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
m.ProviderMetas[provider] = pm
}
for _, e := range file.Encryptions {
if m.Encryption != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate encryption configuration",
Detail: fmt.Sprintf("A module may have only one encryption configuration. Encryption was previously configured at %s.", m.Encryption.DeclRange),
Subject: &e.DeclRange,
})
continue
}
m.Encryption = e
}
for _, v := range file.Variables {
if existing, exists := m.Variables[v.Name]; exists {
diags = append(diags, &hcl.Diagnostic{
@ -552,6 +568,22 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
}
}
if len(file.Encryptions) != 0 {
switch len(file.Encryptions) {
case 1:
m.Encryption = m.Encryption.Merge(file.Encryptions[0])
default:
// An override file with multiple encryptions is still invalid, even
// though it can override encryptions from _other_ files.
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate encryption configuration",
Detail: fmt.Sprintf("Each override file may have only one encryption configuration. Encryption was previously configured at %s.", file.Encryptions[0].DeclRange),
Subject: &file.Encryptions[1].DeclRange,
})
}
}
for _, v := range file.Variables {
existing, exists := m.Variables[v.Name]
if !exists {

View File

@ -7,6 +7,7 @@ package configs
import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/encryption/config"
)
// LoadConfigFile reads the file at the given path and parses it as a config
@ -111,6 +112,13 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
file.ProviderMetas = append(file.ProviderMetas, providerCfg)
}
case "encryption":
encryptionCfg, cfgDiags := config.DecodeConfig(innerBlock.Body, innerBlock.DefRange)
diags = append(diags, cfgDiags...)
if encryptionCfg != nil {
file.Encryptions = append(file.Encryptions, encryptionCfg)
}
default:
// Should never happen because the above cases should be exhaustive
// for all block type names in our schema.

25
internal/configs/shim.go Normal file
View File

@ -0,0 +1,25 @@
package configs
import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/zclconf/go-cty/cty"
)
// These were all moved to the hcl2shim package, but still have uses referenced from this package
// TODO Call sites through opentofu to these functions should be migrated to hcl2shim eventually and this file removed
func MergeBodies(base, override hcl.Body) hcl.Body {
return hcl2shim.MergeBodies(base, override)
}
func exprIsNativeQuotedString(expr hcl.Expression) bool {
return hcl2shim.ExprIsNativeQuotedString(expr)
}
func schemaForOverrides(schema *hcl.BodySchema) *hcl.BodySchema {
return hcl2shim.SchemaForOverrides(schema)
}
func SynthBody(filename string, values map[string]cty.Value) hcl.Body {
return hcl2shim.SynthBody(filename, values)
}

View File

@ -11,9 +11,9 @@ import (
"github.com/opentofu/opentofu/internal/encryption/method"
)
// Config describes the terraform.encryption HCL block you can use to configure the state and plan encryption.
// EncryptionConfig describes the terraform.encryption HCL block you can use to configure the state and plan encryption.
// The individual fields of this struct match the HCL structure directly.
type Config struct {
type EncryptionConfig struct {
KeyProviderConfigs []KeyProviderConfig `hcl:"key_provider,block"`
MethodConfigs []MethodConfig `hcl:"method,block"`
@ -21,11 +21,14 @@ type Config struct {
StateFile *EnforcableTargetConfig `hcl:"statefile,block"`
PlanFile *EnforcableTargetConfig `hcl:"planfile,block"`
Remote *RemoteConfig `hcl:"remote_data_source,block"`
// Not preserved through merge operations
DeclRange hcl.Range
}
// Merge returns a merged configuration with the current config and the specified override combined, the override
// taking precedence.
func (c *Config) Merge(override *Config) *Config {
func (c *EncryptionConfig) Merge(override *EncryptionConfig) *EncryptionConfig {
return MergeConfigs(c, override)
}

View File

@ -18,7 +18,7 @@ import (
// This method serves as an example for how someone using this library might want to load a configuration.
// if they were not using gohcl directly.
// However! Right now, this method should only be used in tests, as OpenTofu should be using gohcl to parse the configuration.
func LoadConfigFromString(sourceName string, rawInput string) (*Config, hcl.Diagnostics) {
func LoadConfigFromString(sourceName string, rawInput string) (*EncryptionConfig, hcl.Diagnostics) {
var diags hcl.Diagnostics
var file *hcl.File

View File

@ -7,12 +7,18 @@ package config
import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
)
// MergeConfigs merges two Configs together, with the override taking precedence.
func MergeConfigs(cfg *Config, override *Config) *Config {
return &Config{
func MergeConfigs(cfg *EncryptionConfig, override *EncryptionConfig) *EncryptionConfig {
if cfg == nil {
return override
}
if override == nil {
return cfg
}
return &EncryptionConfig{
KeyProviderConfigs: mergeKeyProviderConfigs(cfg.KeyProviderConfigs, override.KeyProviderConfigs),
MethodConfigs: mergeMethodConfigs(cfg.MethodConfigs, override.MethodConfigs),
@ -162,5 +168,5 @@ func mergeBody(base hcl.Body, override hcl.Body) hcl.Body {
return base
}
return configs.MergeBodies(base, override)
return hcl2shim.MergeBodies(base, override)
}

View File

@ -12,7 +12,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/zclconf/go-cty/cty"
)
@ -21,7 +21,7 @@ func TestMergeMethodConfigs(t *testing.T) {
return MethodConfig{
Type: typeName,
Name: name,
Body: configs.SynthBody("method", map[string]cty.Value{
Body: hcl2shim.SynthBody("method", map[string]cty.Value{
key: cty.StringVal(value),
}),
}
@ -132,7 +132,7 @@ func TestMergeKeyProviderConfigs(t *testing.T) {
return KeyProviderConfig{
Type: typeName,
Name: name,
Body: configs.SynthBody("key_provider", map[string]cty.Value{
Body: hcl2shim.SynthBody("key_provider", map[string]cty.Value{
key: cty.StringVal(value),
}),
}

View File

@ -16,8 +16,8 @@ import (
// This method is here as an example for how someone using this library might want to decode a configuration.
// if they were not using gohcl directly.
// Right now for real world use this is only intended to be used in tests, until we publish this publicly.
func DecodeConfig(body hcl.Body, rng hcl.Range) (*Config, hcl.Diagnostics) {
cfg := &Config{}
func DecodeConfig(body hcl.Body, rng hcl.Range) (*EncryptionConfig, hcl.Diagnostics) {
cfg := &EncryptionConfig{DeclRange: rng}
diags := gohcl.DecodeBody(body, nil, cfg)
if diags.HasErrors() {

View File

@ -30,12 +30,12 @@ type Encryption interface {
type encryption struct {
// Inputs
cfg *config.Config
cfg *config.EncryptionConfig
reg registry.Registry
}
// New creates a new Encryption provider from the given configuration and registry.
func New(reg registry.Registry, cfg *config.Config) Encryption {
func New(reg registry.Registry, cfg *config.EncryptionConfig) Encryption {
return &encryption{
cfg: cfg,
reg: reg,

View File

@ -18,7 +18,7 @@ import (
)
type targetBuilder struct {
cfg *config.Config
cfg *config.EncryptionConfig
reg registry.Registry
// Used to evaluate hcl expressions