mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-20 02:09:26 -05:00
The "tofu show" command has historically been difficult to extend to meet
new use-cases, such as showing the current configuration without creating
a plan, because it was designed to take zero or one arguments and then try
to guess what the one specified argument was intended to mean.
This commit introduces a new style where the type of object to inspect is
specified using command line option syntax, using one of two
mutually-exclusive options:
-state Show the latest state snapshot.
-plan=FILE Show the plan from the given saved plan file.
We expect that a future commit will extend this with a new "-config" option
to inspect the configuration rooted in the current working directory, and
possibly with "-module=DIR" to shallowly inspect a single module without
necessarily having to fully initialize it with all of its dependencies
first. However, both of those use-cases (and any others) are not in scope
for this commit, which is focused only on refactoring to make those future
use-cases possible.
The old mode of specifying neither option and providing zero or one
positional arguments is still supported for backward compatibility.
Notably, the legacy style is the only way to access the legacy behavior of
inspecting a specific state snapshot file from the local filesystem, which
has not often been used since Terraform v0.9 as we've moved away
from manual management of state files to the structure of state backends.
Those who _do_ still need that old behavior can still access it in the
old way, but there will be no new-style equivalent of it unless we learn
of a compelling use case for it.
Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
143 lines
4.2 KiB
Go
143 lines
4.2 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 arguments
|
|
|
|
import (
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
// Show represents the command-line arguments for the show command.
|
|
type Show struct {
|
|
// TargetType and TargetArg together describe the object that was
|
|
// requested to be shown.
|
|
//
|
|
// The meaning of TargetArg varies depending on TargetType. Refer to
|
|
// the documentation for each [ShowTargetType] constant for details.
|
|
TargetType ShowTargetType
|
|
TargetArg string
|
|
|
|
// ViewType specifies which output format to use: human, JSON, or "raw".
|
|
ViewType ViewType
|
|
|
|
Vars *Vars
|
|
|
|
// ShowSensitive is used to display the value of variables marked as sensitive.
|
|
ShowSensitive bool
|
|
}
|
|
|
|
// ShowTargetType represents the type of object that is requested to be
|
|
// shown by the "tofu show" command.
|
|
type ShowTargetType int
|
|
|
|
//go:generate go run golang.org/x/tools/cmd/stringer -type=ShowTargetType
|
|
const (
|
|
// ShowUnknownType is the zero value of [ShowTargetType], and represents
|
|
// that the target type is ambiguous and so must be inferred by the
|
|
// caller based on the [Show.TargetArg] value.
|
|
ShowUnknownType ShowTargetType = iota
|
|
|
|
// ShowState represents a request to show the latest state snapshot.
|
|
//
|
|
// This target type does not use [Show.TargetArg].
|
|
ShowState
|
|
|
|
// ShowPlan represents a request to show a plan loaded from a saved
|
|
// plan file.
|
|
//
|
|
// For this target type, [Show.TargetArg] is the plan file to load.
|
|
ShowPlan
|
|
)
|
|
|
|
// ParseShow processes CLI arguments, returning a Show value and errors.
|
|
// If errors are encountered, a Show value is still returned representing
|
|
// the best effort interpretation of the arguments.
|
|
func ParseShow(args []string) (*Show, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
show := &Show{
|
|
Vars: &Vars{},
|
|
}
|
|
|
|
var jsonOutput bool
|
|
var stateTarget bool
|
|
var planTarget string
|
|
cmdFlags := extendedFlagSet("show", nil, nil, show.Vars)
|
|
cmdFlags.BoolVar(&jsonOutput, "json", false, "json")
|
|
cmdFlags.BoolVar(&show.ShowSensitive, "show-sensitive", false, "displays sensitive values")
|
|
cmdFlags.BoolVar(&stateTarget, "state", false, "show the latest state snapshot")
|
|
cmdFlags.StringVar(&planTarget, "plan", "", "show the plan from a saved plan file")
|
|
|
|
if err := cmdFlags.Parse(args); err != nil {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Failed to parse command-line options",
|
|
err.Error(),
|
|
))
|
|
}
|
|
|
|
switch {
|
|
case jsonOutput:
|
|
show.ViewType = ViewJSON
|
|
default:
|
|
show.ViewType = ViewHuman
|
|
}
|
|
|
|
if planTarget == "" && !stateTarget {
|
|
// If neither of the target type options was provided then we're
|
|
// in the legacy mode where the target type is implied by
|
|
// the number of arguments.
|
|
args = cmdFlags.Args()
|
|
switch len(args) {
|
|
case 0:
|
|
show.TargetType = ShowState
|
|
show.TargetArg = ""
|
|
case 1:
|
|
// This case is ambiguous: the argument could be either
|
|
// a saved plan file or a local state snapshot such as
|
|
// the output from "tofu state pull". The caller will need
|
|
// to probe TargetArg to decide which it is.
|
|
show.TargetType = ShowUnknownType
|
|
show.TargetArg = args[0]
|
|
default:
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Too many command line arguments",
|
|
"Expected at most one positional argument for the legacy positional argument mode.",
|
|
))
|
|
}
|
|
return show, diags
|
|
}
|
|
|
|
// The following handles the modern mode where the target type is
|
|
// chosen based on which target type option was used.
|
|
if len(cmdFlags.Args()) != 0 {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Unexpected command line arguments",
|
|
"This command does not expect any positional arguments when using a target-selection option.",
|
|
))
|
|
return show, diags
|
|
}
|
|
targetTypes := 0
|
|
if stateTarget {
|
|
targetTypes++
|
|
show.TargetType = ShowState
|
|
show.TargetArg = ""
|
|
}
|
|
if planTarget != "" {
|
|
targetTypes++
|
|
show.TargetType = ShowPlan
|
|
show.TargetArg = planTarget
|
|
}
|
|
if targetTypes != 1 {
|
|
diags = diags.Append(tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Conflicting object types to show",
|
|
"The -state and -plan=FILENAME options are mutually-exclusive, to specify which kind of object to show.",
|
|
))
|
|
}
|
|
return show, diags
|
|
}
|