mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
We previously added the -config mode for showing the entire assembled configuration tree, including the content of any descendent modules, but that mode requires first running "tofu init" to install all of the provider and module dependencies of the configuration. This new -module=DIR mode returns a subset of the same JSON representation for only a single module that can be generated without first installing any dependencies, making this mode more appropriate for situations like generating documentation for a single module when importing it into the OpenTofu Registry. The registry generation process does not want to endure the overhead of installing other providers and modules when all it actually needs is metadata about the top-level declarations in the module. To minimize the risk to the already-working full-config JSON representation while still reusing most of its code, the implementation details of package jsonconfig are a little awkward here. Since this code changes relatively infrequently and is implementing an external interface subject to compatibility constraints, and since this new behavior is relatively marginal and intended primarily for our own OpenTofu Registry purposes, this is a pragmatic tradeoff that is hopefully compensated for well enough by the code comments that aim to explain what's going on for the benefit of future maintainers. If we _do_ find ourselves making substantial changes to this code at a later date then we can consider a more significant restructure of the code at that point; the weird stuff is intentionally encapsulated inside package jsonconfig so it can change later without changing any callers. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
81 lines
3.5 KiB
Go
81 lines
3.5 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package jsonconfig
|
|
|
|
import (
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
|
"github.com/opentofu/opentofu/internal/tofu"
|
|
)
|
|
|
|
// MarshalSingleModule is a variant of [Marshal] that describes only a single
|
|
// module, without any references to its child modules or associated provider
|
|
// schemas.
|
|
//
|
|
// This uses only a subset of the typical configuration representation, due to
|
|
// schema and child module information being unavailable:
|
|
// - Module calls omit the "module" property that would normally describe the
|
|
// content of the child module.
|
|
// - Resource descriptions omit the "schema_version" property because no
|
|
// schema-based information is included.
|
|
// - Expression-related properties are omitted in all cases. Technically only
|
|
// expressions passed to providers _need_ to be omitted, but for now we
|
|
// just consistently omit all of them because that's an easier rule to
|
|
// explain and avoids exposing what is and is not provider-based so that
|
|
// we could potentially change those details in future.
|
|
func MarshalSingleModule(m *configs.Module) ([]byte, error) {
|
|
// Our shared codepaths are built to work with a full config tree rather
|
|
// than a single module, so we'll construct a synthetic [configs.Config]
|
|
// that only has a root module and then downstream shared functions will
|
|
// use the nil-ness of the schemas argument to handle the special
|
|
// treatments required in single-module mode.
|
|
cfg := &configs.Config{
|
|
Module: m,
|
|
// Everything else intentionally not populated because single module
|
|
// mode should not attempt to access anything else.
|
|
}
|
|
return marshal(cfg, nil)
|
|
}
|
|
|
|
// inSingleModuleMode returns true if the given schema value indicates that
|
|
// we should be rendering in "single module" mode, meaning that we're producing
|
|
// a result for [MarshalSingleModule] rather than [Marshal].
|
|
//
|
|
// Currently the rule is only that a nil schemas represents single-module mode;
|
|
// this simple rule is factored out into this helper function only so we can
|
|
// centralize it underneath this doc comment explaining the special convention.
|
|
//
|
|
// (This rather odd design is a consequence of how this code evolved; we
|
|
// retrofitted the single-module mode later while using this strange treatment
|
|
// to minimize the risk to the existing working codepaths. Maybe we'll change
|
|
// the appoach to this in future; this is only an implementation detail
|
|
// within this package so we'll be able to those changes without affecting
|
|
// callers.)
|
|
func inSingleModuleMode[S schemaObject](schema S) bool {
|
|
return schema == nil
|
|
}
|
|
|
|
// mapSchema is a helper that uses the given function to transform the given
|
|
// schema object only if it isn't nil, or immediately returns nil otherwise.
|
|
//
|
|
// This is part of our strategy to retrofit the single-module mode without
|
|
// a risky refactor of the already-working code, intended to be used in
|
|
// conjunction with [inSingleModuleMode] to smuggle the flag for whether we're
|
|
// in that mode through the nil-ness of the schema objects.
|
|
func mapSchema[In, Out schemaObject](schema In, f func(In) Out) Out {
|
|
if schema == nil {
|
|
return nil
|
|
}
|
|
return f(schema)
|
|
}
|
|
|
|
// schemaObject is a helper interface to allow [inSingleModuleMode] to be
|
|
// generic over the different nilable schema types used by different parts
|
|
// of the implementation in this package.
|
|
type schemaObject interface {
|
|
*tofu.Schemas | *configschema.Block
|
|
}
|