Repository: opentofu/opentofu
File: internal/encryption/keyprovider.go (Go)

Code before:
		}

		keyProviderDeps = append(keyProviderDeps, kpc)
	}

	refs, refDiags := lang.References(addrs.ParseRef, nonKeyProviderDeps)
	diags = diags.Extend(refDiags.ToHCL())

	return keyProviderDeps, refs, diags
}

// setupKeyProviders sets up the key providers for encryption. It returns a list of diagnostics if any of the key providers
// are invalid.
func setupKeyProviders(enc *config.EncryptionConfig, cfgs []config.KeyProviderConfig, meta keyProviderMetadata, reg registry.Registry, staticEval *configs.StaticEvaluator) (*hcl.EvalContext, hcl.Diagnostics) {
	var diags hcl.Diagnostics

	kpData := make(valueMap)

	for _, keyProviderConfig := range cfgs {
		diags = diags.Extend(setupKeyProvider(enc, keyProviderConfig, kpData, nil, meta, reg, staticEval))
	}

	return kpData.hclEvalContext("key_provider"), diags
}

func keyProvidersStack(stack []config.KeyProviderConfig) ([]string, hcl.Diagnostics) {
	res := make([]string, len(stack))
	var diags hcl.Diagnostics
	for i, cfg := range stack {
		addr, diag := cfg.Addr()
		diags = diags.Extend(diag)
		if diag.HasErrors() {
			res[i] = "<unknown>"
			continue
		}
		res[i] = string(addr)
	}
	return res, diags
}

func setupKeyProvider(enc *config.EncryptionConfig, cfg config.KeyProviderConfig, kpData valueMap, stack []config.KeyProviderConfig, meta keyProviderMetadata, reg registry.Registry, staticEval *configs.StaticEvaluator) hcl.Diagnostics {
	// Check if we have already setup this Descriptor (due to dependency loading)
	// if we've already setup this key provider, then we don't need to do it again
	// and we can return early
	if kpData.has(cfg.Type, cfg.Name) {
		return nil
	}

	// Mark this key provider as partially handled.  This value will be replaced below once it is actually known.
	// The goal is to allow an early return via the above if statement to prevent duplicate errors if errors are encountered in the key loading stack.
	kpData.set(cfg.Type, cfg.Name, cty.UnknownVal(cty.DynamicPseudoType))

Review comment (suggestion):
`suggestion`
Would be better for the readability of the code to have this function moved at the bottom of the file. This is more of a personal preference, so that's why I tagged this as `suggestion`.

Code after:
		}

		keyProviderDeps = append(keyProviderDeps, kpc)
	}

	refs, refDiags := lang.References(addrs.ParseRef, nonKeyProviderDeps)
	diags = diags.Extend(refDiags.ToHCL())

	return keyProviderDeps, refs, diags
}

// setupKeyProviders sets up the key providers for encryption. It returns a list of diagnostics if any of the key providers
// are invalid.
func setupKeyProviders(enc *config.EncryptionConfig, cfgs []config.KeyProviderConfig, meta keyProviderMetadata, reg registry.Registry, staticEval *configs.StaticEvaluator) (*hcl.EvalContext, hcl.Diagnostics) {
	var diags hcl.Diagnostics

	kpData := make(valueMap)

	for _, keyProviderConfig := range cfgs {
		diags = diags.Extend(setupKeyProvider(enc, keyProviderConfig, kpData, nil, meta, reg, staticEval))
	}

	return kpData.hclEvalContext("key_provider"), diags
}

func setupKeyProvider(enc *config.EncryptionConfig, cfg config.KeyProviderConfig, kpData valueMap, stack []config.KeyProviderConfig, meta keyProviderMetadata, reg registry.Registry, staticEval *configs.StaticEvaluator) hcl.Diagnostics {
	// Check if we have already setup this Descriptor (due to dependency loading)
	// if we've already setup this key provider, then we don't need to do it again
	// and we can return early
	if kpData.has(cfg.Type, cfg.Name) {
		return nil
	}

	// Mark this key provider as partially handled.  This value will be replaced below once it is actually known.
	// The goal is to allow an early return via the above if statement to prevent duplicate errors if errors are encountered in the key loading stack.
	kpData.set(cfg.Type, cfg.Name, cty.UnknownVal(cty.DynamicPseudoType))

	// Check for circular references, this is done by inspecting the stack of key providers
	// that are currently being setup. If we find a key provider in the stack that matches
	// the current key provider, then we have a circular reference and we should return an error
	// to the user.
	for _, s := range stack {
		if s == cfg {
			addr, diags := keyprovider.NewAddr(cfg.Type, cfg.Name)
			stackAddrs, diag := keyProvidersStack(append(stack, cfg))
			diags = diags.Extend(diag)
			return diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Circular reference detected",
				Detail:   fmt.Sprintf("Cannot load %s due to circular reference between key providers. Stack trace %s", addr, strings.Join(stackAddrs, " -> ")),
			})