blob: 19eab321fa18c1b34ca319d18875b1c5755fa9e9 [file] [log] [blame] [edit]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package command
import (
"errors"
"fmt"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/modsdir"
"github.com/hashicorp/terraform/internal/moduleref"
"github.com/hashicorp/terraform/internal/terraform"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// ModulesCommand is a Command implementation that prints out information
// about the modules declared by the current configuration.
type ModulesCommand struct {
Meta
viewType arguments.ViewType
}
func (c *ModulesCommand) Help() string {
return modulesCommandHelp
}
func (c *ModulesCommand) Synopsis() string {
return "Show all declared modules in a working directory"
}
func (c *ModulesCommand) Run(rawArgs []string) int {
// Parse global view arguments
rawArgs = c.Meta.process(rawArgs)
common, rawArgs := arguments.ParseView(rawArgs)
c.View.Configure(common)
// Parse command specific flags
args, diags := arguments.ParseModules(rawArgs)
if diags.HasErrors() {
c.View.Diagnostics(diags)
c.View.HelpPrompt("modules")
return 1
}
c.viewType = args.ViewType
// Set up the command's view
view := views.NewModules(c.viewType, c.View)
rootModPath, err := ModulePath([]string{})
if err != nil {
diags = diags.Append(err)
view.Diagnostics(diags)
return 1
}
// Read the root module path so we can then traverse the tree
rootModEarly, earlyConfDiags := c.loadSingleModule(rootModPath)
if rootModEarly == nil {
diags = diags.Append(errors.New("root module not found. Please run terraform init"), earlyConfDiags)
view.Diagnostics(diags)
return 1
}
config, confDiags := c.loadConfig(rootModPath)
// Here we check if there are any uninstalled dependencies
versionDiags := terraform.CheckCoreVersionRequirements(config)
if versionDiags.HasErrors() {
view.Diagnostics(versionDiags)
return 1
}
diags = diags.Append(earlyConfDiags)
if earlyConfDiags.HasErrors() {
view.Diagnostics(diags)
return 1
}
diags = diags.Append(confDiags)
if confDiags.HasErrors() {
view.Diagnostics(diags)
return 1
}
// Fetch the module manifest
internalManifest, diags := c.internalManifest()
if diags.HasErrors() {
view.Diagnostics(diags)
return 1
}
// Create a module reference resolver
resolver := moduleref.NewResolver(internalManifest)
// Crawl the Terraform config and find entries with references
manifestWithRef := resolver.Resolve(config)
// Render the new manifest with references
return view.Display(*manifestWithRef)
}
// internalManifest will use the configuration loader to refresh and load the
// internal manifest.
func (c *ModulesCommand) internalManifest() (modsdir.Manifest, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
loader, err := c.initConfigLoader()
if err != nil {
diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %w", err))
return nil, diags
}
if err = loader.RefreshModules(); err != nil {
diags = diags.Append(fmt.Errorf("Failed to refresh module manifest: %w", err))
return nil, diags
}
return loader.ModuleManifest(), diags
}
const modulesCommandHelp = `
Usage: terraform [global options] modules [options]
Prints out a list of all declared Terraform modules and their resolved versions
in a Terraform working directory.
Options:
-json If specified, output declared Terraform modules and
their resolved versions in a machine-readable format.
`