Files
opentf/internal/command/arguments/show.go
Christian Mesh 6280691025 Support dual output streams in most commands (#3606)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
Co-authored-by: Diógenes Fernandes <diofeher@gmail.com>
2026-01-20 14:08:10 -05:00

183 lines
5.6 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
// ViewOptions specifies which view options to use
ViewOptions ViewOptions
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 tool 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
// ShowConfig represents a request to show the current configuration.
//
// This target type does not use [Show.TargetArg].
ShowConfig
// ShowModule represents a request to show just one module in isolation,
// without requiring any of its dependencies to be installed.
//
// For this target type, [Show.TargetArg] is a path to the directory
// containing the module.
ShowModule
)
// ParseShow processes CLI arguments, returning a Show value, a closer function, 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, func(), tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
show := &Show{
Vars: &Vars{},
}
var stateTarget bool
var planTarget string
var configTarget bool
var moduleTarget string
cmdFlags := extendedFlagSet("show", nil, nil, show.Vars)
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")
cmdFlags.BoolVar(&configTarget, "config", false, "show the current configuration")
cmdFlags.StringVar(&moduleTarget, "module", "", "show metadata about one module")
show.ViewOptions.AddFlags(cmdFlags, false)
if err := cmdFlags.Parse(args); err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to parse command-line options",
err.Error(),
))
}
closer, moreDiags := show.ViewOptions.Parse()
diags = diags.Append(moreDiags)
// If -config or -module=... is selected, -json is required
if configTarget && !show.ViewOptions.jsonFlag {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"JSON output required for configuration",
"The -config option requires -json to be specified.",
))
return show, closer, diags
}
if moduleTarget != "" && !show.ViewOptions.jsonFlag {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"JSON output required for module",
"The -module=DIR option requires -json to be specified.",
))
return show, closer, diags
}
if planTarget == "" && moduleTarget == "" && !stateTarget && !configTarget {
// If none 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, closer, 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, closer, diags
}
targetTypes := 0
if stateTarget {
targetTypes++
show.TargetType = ShowState
show.TargetArg = ""
}
if planTarget != "" {
targetTypes++
show.TargetType = ShowPlan
show.TargetArg = planTarget
}
if configTarget {
targetTypes++
show.TargetType = ShowConfig
show.TargetArg = ""
}
if moduleTarget != "" {
targetTypes++
show.TargetType = ShowModule
show.TargetArg = moduleTarget
}
if targetTypes != 1 {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Conflicting object types to show",
"The -state, -plan=FILENAME, -config, and -module=DIR options are mutually-exclusive, to specify which kind of object to show.",
))
}
return show, closer, diags
}