mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
command: "console" now accepts a -scope argument
The -scope argument specifies a scope other than the root module to evaluate expressions in. Currently we only support module instance addresses as scope addresses but this is generalized to allow potentially supporting other evaluation scopes in future if we find reasons to support that, such as evaluating in the scope of a particular resource instance to find out what its each.value is. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
parent
3de0333560
commit
b7038ef037
@ -29,10 +29,12 @@ type ConsoleCommand struct {
|
||||
|
||||
func (c *ConsoleCommand) Run(args []string) int {
|
||||
ctx := c.CommandContext()
|
||||
var scopeAddrStr string
|
||||
|
||||
args = c.Meta.process(args)
|
||||
cmdFlags := c.Meta.extendedFlagSet("console")
|
||||
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
|
||||
cmdFlags.StringVar(&scopeAddrStr, "scope", "", "evaluation scope address")
|
||||
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error parsing command line flags: %s\n", err.Error()))
|
||||
@ -54,6 +56,18 @@ func (c *ConsoleCommand) Run(args []string) int {
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
scopeAddr := addrs.ExprScope(addrs.RootModuleInstance)
|
||||
if scopeAddrStr != "" {
|
||||
// User is trying to specify a scope other than the root module.
|
||||
var scopeAddrDiags tfdiags.Diagnostics
|
||||
scopeAddr, scopeAddrDiags = addrs.ParseExprScopeStr(scopeAddrStr)
|
||||
diags = diags.Append(scopeAddrDiags)
|
||||
if scopeAddrDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Load the encryption configuration
|
||||
enc, encDiags := c.EncryptionFromPath(configPath)
|
||||
diags = diags.Append(encDiags)
|
||||
@ -146,7 +160,7 @@ func (c *ConsoleCommand) Run(args []string) int {
|
||||
// Before we can evaluate expressions, we must compute and populate any
|
||||
// derived values (input variables, local values, output values)
|
||||
// that are not stored in the persistent state.
|
||||
scope, scopeDiags := lr.Core.Eval(ctx, lr.Config, lr.InputState, addrs.RootModuleInstance, evalOpts)
|
||||
scope, scopeDiags := lr.Core.Eval(ctx, lr.Config, lr.InputState, scopeAddr, evalOpts)
|
||||
diags = diags.Append(scopeDiags)
|
||||
if scope == nil {
|
||||
// scope is nil if there are errors so bad that we can't even build a scope.
|
||||
@ -214,12 +228,12 @@ func (c *ConsoleCommand) Help() string {
|
||||
Usage: tofu [global options] console [options]
|
||||
|
||||
Starts an interactive console for experimenting with OpenTofu
|
||||
interpolations.
|
||||
expressions.
|
||||
|
||||
This will open an interactive console that you can use to type
|
||||
interpolations into and inspect their values. This command loads the
|
||||
current state. This lets you explore and test interpolations before
|
||||
using them in future configurations.
|
||||
expressions into and inspect their values. This command loads the
|
||||
current state. This lets you explore and test expressions before
|
||||
using them in your modules.
|
||||
|
||||
This command will never modify your state.
|
||||
|
||||
@ -237,6 +251,11 @@ Options:
|
||||
will be performed. All locations, for all errors
|
||||
will be listed. Disabled by default
|
||||
|
||||
-scope=ADDR Choose the scope where expressions will be evaluated.
|
||||
Currently this must be a module instance address.
|
||||
If unspecified, expressions are evaluated in the root
|
||||
module's scope.
|
||||
|
||||
-state=path Legacy option for the local backend only. See the local
|
||||
backend's documentation for more information.
|
||||
|
||||
|
@ -243,6 +243,50 @@ func TestConsole_modules(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsole_modulesNestedScope(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath("modules"), td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
p := applyFixtureProvider()
|
||||
ui := cli.NewMockUi()
|
||||
view, _ := testView(t)
|
||||
|
||||
c := &ConsoleCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
Ui: ui,
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{"-scope=module.count_child[0]"}
|
||||
commands := map[string]string{
|
||||
// Since the resource instance hasn't been applied yet it
|
||||
// returns an unknown value, but the important thing here
|
||||
// is that it doesn't fail because test_instance.test is
|
||||
// declared inside module.count_child[0] and is
|
||||
// therefore valid to refer to in that scope.
|
||||
"test_instance.test\n": "(known after apply)\n",
|
||||
}
|
||||
|
||||
for cmd, val := range commands {
|
||||
var output bytes.Buffer
|
||||
defer testStdinPipe(t, strings.NewReader(cmd))()
|
||||
outCloser := testStdoutCapture(t, &output)
|
||||
code := c.Run(args)
|
||||
outCloser()
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
actual := output.String()
|
||||
if output.String() != val {
|
||||
t.Fatalf("bad: %q, expected %q", actual, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsole_multiline_pipe(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath("console-multiline-vars"), td)
|
||||
|
Loading…
Reference in New Issue
Block a user