mirror of
https://github.com/hashicorp/terraform.git
synced 2026-02-18 18:29:44 -05:00
move providers lock command to new arguments / views architecture
This commit is contained in:
parent
27770ee805
commit
c7f53388bf
6 changed files with 511 additions and 79 deletions
70
internal/command/arguments/providers_lock.go
Normal file
70
internal/command/arguments/providers_lock.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
// ProvidersLock represents the command-line arguments for the providers lock command.
|
||||
type ProvidersLock struct {
|
||||
// Platforms is the list of target platforms to request package checksums for.
|
||||
Platforms FlagStringSlice
|
||||
|
||||
// FSMirrorDir is the filesystem mirror directory to consult instead of the
|
||||
// origin registry.
|
||||
FSMirrorDir string
|
||||
|
||||
// NetMirrorURL is the network mirror base URL to consult instead of the
|
||||
// origin registry.
|
||||
NetMirrorURL string
|
||||
|
||||
// TestDirectory is the directory containing test files, defaults to "tests".
|
||||
TestDirectory string
|
||||
|
||||
// EnablePluginCache enables the usage of the globally configured plugin cache.
|
||||
EnablePluginCache bool
|
||||
|
||||
// Providers is the list of provider source addresses given as positional arguments.
|
||||
Providers []string
|
||||
}
|
||||
|
||||
// ParseProvidersLock processes CLI arguments, returning a ProvidersLock value and error
|
||||
// diagnostics. If there are any diagnostics present, a ProvidersLock value is still
|
||||
// returned representing the best effort interpretation of the arguments.
|
||||
func ParseProvidersLock(args []string) (*ProvidersLock, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
result := &ProvidersLock{
|
||||
TestDirectory: "tests",
|
||||
}
|
||||
|
||||
cmdFlags := defaultFlagSet("providers lock")
|
||||
cmdFlags.Var(&result.Platforms, "platform", "target platform")
|
||||
cmdFlags.StringVar(&result.FSMirrorDir, "fs-mirror", "", "filesystem mirror directory")
|
||||
cmdFlags.StringVar(&result.NetMirrorURL, "net-mirror", "", "network mirror base URL")
|
||||
cmdFlags.StringVar(&result.TestDirectory, "test-directory", "tests", "test-directory")
|
||||
cmdFlags.BoolVar(&result.EnablePluginCache, "enable-plugin-cache", false, "enable plugin cache")
|
||||
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to parse command-line flags",
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
if remaining := cmdFlags.Args(); len(remaining) > 0 {
|
||||
result.Providers = remaining
|
||||
}
|
||||
|
||||
if result.FSMirrorDir != "" && result.NetMirrorURL != "" {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid installation method options",
|
||||
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
|
||||
))
|
||||
}
|
||||
|
||||
return result, diags
|
||||
}
|
||||
142
internal/command/arguments/providers_lock_test.go
Normal file
142
internal/command/arguments/providers_lock_test.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package arguments
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func TestParseProvidersLock_valid(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
want *ProvidersLock
|
||||
}{
|
||||
"defaults": {
|
||||
nil,
|
||||
&ProvidersLock{
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"fs-mirror": {
|
||||
[]string{"-fs-mirror=/path/to/mirror"},
|
||||
&ProvidersLock{
|
||||
FSMirrorDir: "/path/to/mirror",
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"net-mirror": {
|
||||
[]string{"-net-mirror=https://mirror.example.com/"},
|
||||
&ProvidersLock{
|
||||
NetMirrorURL: "https://mirror.example.com/",
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"single platform": {
|
||||
[]string{"-platform=linux_amd64"},
|
||||
&ProvidersLock{
|
||||
Platforms: FlagStringSlice{"linux_amd64"},
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"multiple platforms": {
|
||||
[]string{"-platform=linux_amd64", "-platform=darwin_arm64"},
|
||||
&ProvidersLock{
|
||||
Platforms: FlagStringSlice{"linux_amd64", "darwin_arm64"},
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"enable-plugin-cache": {
|
||||
[]string{"-enable-plugin-cache"},
|
||||
&ProvidersLock{
|
||||
EnablePluginCache: true,
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"test-directory": {
|
||||
[]string{"-test-directory=mytests"},
|
||||
&ProvidersLock{
|
||||
TestDirectory: "mytests",
|
||||
},
|
||||
},
|
||||
"provider arguments": {
|
||||
[]string{"hashicorp/aws", "hashicorp/random"},
|
||||
&ProvidersLock{
|
||||
Providers: []string{"hashicorp/aws", "hashicorp/random"},
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
},
|
||||
"all options": {
|
||||
[]string{"-fs-mirror=/mirror", "-platform=linux_amd64", "-enable-plugin-cache", "-test-directory=mytests", "hashicorp/aws"},
|
||||
&ProvidersLock{
|
||||
FSMirrorDir: "/mirror",
|
||||
Platforms: FlagStringSlice{"linux_amd64"},
|
||||
EnablePluginCache: true,
|
||||
TestDirectory: "mytests",
|
||||
Providers: []string{"hashicorp/aws"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, diags := ParseProvidersLock(tc.args)
|
||||
if len(diags) > 0 {
|
||||
t.Fatalf("unexpected diags: %v", diags)
|
||||
}
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseProvidersLock_invalid(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
want *ProvidersLock
|
||||
wantDiags tfdiags.Diagnostics
|
||||
}{
|
||||
"unknown flag": {
|
||||
[]string{"-unknown"},
|
||||
&ProvidersLock{
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to parse command-line flags",
|
||||
"flag provided but not defined: -unknown",
|
||||
),
|
||||
},
|
||||
},
|
||||
"mirror collision": {
|
||||
[]string{"-fs-mirror=/foo/", "-net-mirror=https://example.com/"},
|
||||
&ProvidersLock{
|
||||
FSMirrorDir: "/foo/",
|
||||
NetMirrorURL: "https://example.com/",
|
||||
TestDirectory: "tests",
|
||||
},
|
||||
tfdiags.Diagnostics{
|
||||
tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid installation method options",
|
||||
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, gotDiags := ParseProvidersLock(tc.args)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want)
|
||||
}
|
||||
tfdiags.AssertDiagnosticsMatch(t, gotDiags, tc.wantDiags)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/command/arguments"
|
||||
"github.com/hashicorp/terraform/internal/command/views"
|
||||
"github.com/hashicorp/terraform/internal/depsfile"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
"github.com/hashicorp/terraform/internal/providercache"
|
||||
|
|
@ -38,45 +39,33 @@ func (c *ProvidersLockCommand) Synopsis() string {
|
|||
return "Write out dependency locks for the configured providers"
|
||||
}
|
||||
|
||||
func (c *ProvidersLockCommand) Run(args []string) int {
|
||||
args = c.Meta.process(args)
|
||||
cmdFlags := c.Meta.defaultFlagSet("providers lock")
|
||||
var optPlatforms arguments.FlagStringSlice
|
||||
var fsMirrorDir string
|
||||
var netMirrorURL string
|
||||
var testDirectory string
|
||||
func (c *ProvidersLockCommand) Run(rawArgs []string) int {
|
||||
// Parse and apply global view arguments
|
||||
common, rawArgs := arguments.ParseView(rawArgs)
|
||||
c.View.Configure(common)
|
||||
|
||||
cmdFlags.Var(&optPlatforms, "platform", "target platform")
|
||||
cmdFlags.StringVar(&fsMirrorDir, "fs-mirror", "", "filesystem mirror directory")
|
||||
cmdFlags.StringVar(&netMirrorURL, "net-mirror", "", "network mirror base URL")
|
||||
cmdFlags.StringVar(&testDirectory, "test-directory", "tests", "test-directory")
|
||||
pluginCache := cmdFlags.Bool("enable-plugin-cache", false, "")
|
||||
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()))
|
||||
// Propagate -no-color for legacy use of Ui
|
||||
c.Meta.color = !common.NoColor
|
||||
c.Meta.Color = c.Meta.color
|
||||
|
||||
// Parse and validate command-specific flags
|
||||
args, diags := arguments.ParseProvidersLock(rawArgs)
|
||||
|
||||
// Instantiate the view, even if there are flag errors
|
||||
view := views.NewProvidersLock(c.View)
|
||||
|
||||
if diags.HasErrors() {
|
||||
view.Diagnostics(diags)
|
||||
view.HelpPrompt()
|
||||
return 1
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
if fsMirrorDir != "" && netMirrorURL != "" {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid installation method options",
|
||||
"The -fs-mirror and -net-mirror command line options are mutually-exclusive.",
|
||||
))
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
providerStrs := cmdFlags.Args()
|
||||
|
||||
var platforms []getproviders.Platform
|
||||
if len(optPlatforms) == 0 {
|
||||
if len(args.Platforms) == 0 {
|
||||
platforms = []getproviders.Platform{getproviders.CurrentPlatform}
|
||||
} else {
|
||||
platforms = make([]getproviders.Platform, 0, len(optPlatforms))
|
||||
for _, platformStr := range optPlatforms {
|
||||
platforms = make([]getproviders.Platform, 0, len(args.Platforms))
|
||||
for _, platformStr := range args.Platforms {
|
||||
platform, err := getproviders.ParsePlatform(platformStr)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
|
|
@ -104,17 +93,17 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
// against the upstream checksums.
|
||||
var source getproviders.Source
|
||||
switch {
|
||||
case fsMirrorDir != "":
|
||||
source = getproviders.NewFilesystemMirrorSource(fsMirrorDir)
|
||||
case netMirrorURL != "":
|
||||
u, err := url.Parse(netMirrorURL)
|
||||
case args.FSMirrorDir != "":
|
||||
source = getproviders.NewFilesystemMirrorSource(args.FSMirrorDir)
|
||||
case args.NetMirrorURL != "":
|
||||
u, err := url.Parse(args.NetMirrorURL)
|
||||
if err != nil || u.Scheme != "https" {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid network mirror URL",
|
||||
"The -net-mirror option requires a valid https: URL as the mirror base URL.",
|
||||
))
|
||||
c.showDiagnostics(diags)
|
||||
view.Diagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
source = getproviders.NewHTTPMirrorSource(u, c.Services.CredentialsSource())
|
||||
|
|
@ -125,7 +114,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
source = getproviders.NewRegistrySource(c.Services)
|
||||
}
|
||||
|
||||
config, confDiags := c.loadConfigWithTests(".", testDirectory)
|
||||
config, confDiags := c.loadConfigWithTests(".", args.TestDirectory)
|
||||
diags = diags.Append(confDiags)
|
||||
reqs, hclDiags := config.ProviderRequirements()
|
||||
diags = diags.Append(hclDiags)
|
||||
|
|
@ -134,9 +123,9 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
// we'll modify "reqs" to only include those. Modifying this is okay
|
||||
// because config.ProviderRequirements generates a fresh map result
|
||||
// for each call.
|
||||
if len(providerStrs) != 0 {
|
||||
if len(args.Providers) != 0 {
|
||||
providers := map[addrs.Provider]struct{}{}
|
||||
for _, raw := range providerStrs {
|
||||
for _, raw := range args.Providers {
|
||||
addr, moreDiags := addrs.ParseProviderSourceString(raw)
|
||||
diags = diags.Append(moreDiags)
|
||||
if moreDiags.HasErrors() {
|
||||
|
|
@ -176,7 +165,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
|
||||
// If we have any error diagnostics already then we won't proceed further.
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
view.Diagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +199,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
// Our output from this command is minimal just to show that
|
||||
// we're making progress, rather than just silently hanging.
|
||||
FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, loc getproviders.PackageLocation) {
|
||||
c.Ui.Output(fmt.Sprintf("- Fetching %s %s for %s...", provider.ForDisplay(), version, platform))
|
||||
view.Fetching(provider, version, platform)
|
||||
if prevVersion, exists := selectedVersions[provider]; exists && version != prevVersion {
|
||||
// This indicates a weird situation where we ended up
|
||||
// selecting a different version for one platform than
|
||||
|
|
@ -240,10 +229,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
if auth != nil && auth.ThirdPartySigned() {
|
||||
keyID = auth.KeyID
|
||||
}
|
||||
if keyID != "" {
|
||||
keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("- Retrieved %s %s for %s (%s%s)", provider.ForDisplay(), version, platform, auth, keyID))
|
||||
view.FetchSuccess(provider, version, platform, auth.String(), keyID)
|
||||
},
|
||||
}
|
||||
ctx := evts.OnContext(ctx)
|
||||
|
|
@ -253,7 +239,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
|
||||
// Use global plugin cache for extra speed if present and flag is set
|
||||
globalCacheDir := c.providerGlobalCacheDir()
|
||||
if *pluginCache && globalCacheDir != nil {
|
||||
if args.EnablePluginCache && globalCacheDir != nil {
|
||||
installer.SetGlobalCacheDir(globalCacheDir.WithPlatform(platform))
|
||||
installer.SetGlobalCacheDirMayBreakDependencyLockFile(c.PluginCacheMayBreakDependencyLockFile)
|
||||
}
|
||||
|
|
@ -273,7 +259,7 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
// If we have any error diagnostics from installation then we won't
|
||||
// proceed to merging and updating the lock file on disk.
|
||||
if diags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
view.Diagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
@ -318,24 +304,12 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
switch providersLockCalculateChangeType(oldLock, platformLock) {
|
||||
case providersLockChangeTypeNewProvider:
|
||||
madeAnyChange = true
|
||||
c.Ui.Output(
|
||||
fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; This was a new provider and the checksums for this platform are now tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
view.NewProvider(provider, platform)
|
||||
case providersLockChangeTypeNewHashes:
|
||||
madeAnyChange = true
|
||||
c.Ui.Output(
|
||||
fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; Additional checksums for this platform are now tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
view.NewHashes(provider, platform)
|
||||
case providersLockChangeTypeNoChange:
|
||||
c.Ui.Output(
|
||||
fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; All checksums for this platform were already tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
view.ExistingHashes(provider, platform)
|
||||
}
|
||||
}
|
||||
newLocks.SetProvider(provider, version, constraints, hashes)
|
||||
|
|
@ -344,17 +318,12 @@ func (c *ProvidersLockCommand) Run(args []string) int {
|
|||
moreDiags = c.replaceLockedDependencies(newLocks)
|
||||
diags = diags.Append(moreDiags)
|
||||
|
||||
c.showDiagnostics(diags)
|
||||
view.Diagnostics(diags)
|
||||
if diags.HasErrors() {
|
||||
return 1
|
||||
}
|
||||
|
||||
if madeAnyChange {
|
||||
c.Ui.Output(c.Colorize().Color("\n[bold][green]Success![reset] [bold]Terraform has updated the lock file.[reset]"))
|
||||
c.Ui.Output("\nReview the changes in .terraform.lock.hcl and then commit to your\nversion control system to retain the new checksums.\n")
|
||||
} else {
|
||||
c.Ui.Output(c.Colorize().Color("\n[bold][green]Success![reset] [bold]Terraform has validated the lock file and found no need for changes.[reset]"))
|
||||
}
|
||||
view.Success(madeAnyChange)
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ func TestProvidersLock(t *testing.T) {
|
|||
t.Chdir(td)
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
view, _ := testView(t)
|
||||
c := &ProvidersLockCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
Ui: ui,
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
code := c.Run([]string{})
|
||||
|
|
@ -119,9 +121,11 @@ func runProviderLockGenericTest(t *testing.T, testDirectory, expected string, in
|
|||
|
||||
p := testProvider()
|
||||
ui := new(cli.MockUi)
|
||||
view, _ := testView(t)
|
||||
c := &ProvidersLockCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
View: view,
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
},
|
||||
}
|
||||
|
|
@ -146,9 +150,11 @@ func TestProvidersLock_args(t *testing.T) {
|
|||
|
||||
t.Run("mirror collision", func(t *testing.T) {
|
||||
ui := new(cli.MockUi)
|
||||
view, done := testView(t)
|
||||
c := &ProvidersLockCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
Ui: ui,
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +168,7 @@ func TestProvidersLock_args(t *testing.T) {
|
|||
if code != 1 {
|
||||
t.Fatalf("wrong exit code; expected 1, got %d", code)
|
||||
}
|
||||
output := ui.ErrorWriter.String()
|
||||
output := done(t).Stderr()
|
||||
if !strings.Contains(output, "The -fs-mirror and -net-mirror command line options are mutually-exclusive.") {
|
||||
t.Fatalf("missing expected error message: %s", output)
|
||||
}
|
||||
|
|
@ -170,9 +176,11 @@ func TestProvidersLock_args(t *testing.T) {
|
|||
|
||||
t.Run("invalid platform", func(t *testing.T) {
|
||||
ui := new(cli.MockUi)
|
||||
view, done := testView(t)
|
||||
c := &ProvidersLockCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
Ui: ui,
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -183,17 +191,19 @@ func TestProvidersLock_args(t *testing.T) {
|
|||
if code != 1 {
|
||||
t.Fatalf("wrong exit code; expected 1, got %d", code)
|
||||
}
|
||||
output := ui.ErrorWriter.String()
|
||||
if !strings.Contains(output, "must be two words separated by an underscore.") {
|
||||
output := done(t).Stderr()
|
||||
if !strings.Contains(output, "not a valid target platform") {
|
||||
t.Fatalf("missing expected error message: %s", output)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid provider argument", func(t *testing.T) {
|
||||
ui := new(cli.MockUi)
|
||||
view, done := testView(t)
|
||||
c := &ProvidersLockCommand{
|
||||
Meta: Meta{
|
||||
Ui: ui,
|
||||
Ui: ui,
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -204,8 +214,8 @@ func TestProvidersLock_args(t *testing.T) {
|
|||
if code != 1 {
|
||||
t.Fatalf("wrong exit code; expected 1, got %d", code)
|
||||
}
|
||||
output := ui.ErrorWriter.String()
|
||||
if !strings.Contains(output, "The provider registry.terraform.io/hashicorp/random is not required by the\ncurrent configuration.") {
|
||||
output := done(t).Stderr()
|
||||
if !strings.Contains(output, "is not required by") {
|
||||
t.Fatalf("missing expected error message: %s", output)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
100
internal/command/views/providers_lock.go
Normal file
100
internal/command/views/providers_lock.go
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
// ProvidersLock is the view interface for the providers lock command.
|
||||
type ProvidersLock interface {
|
||||
// Fetching announces that a provider package is being fetched.
|
||||
Fetching(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform)
|
||||
|
||||
// FetchSuccess announces that a provider package was fetched successfully.
|
||||
FetchSuccess(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform, auth string, keyID string)
|
||||
|
||||
// NewProvider announces that checksums for a new provider have been added to the lock file.
|
||||
NewProvider(provider addrs.Provider, platform getproviders.Platform)
|
||||
|
||||
// NewHashes announces that additional checksums have been added for an existing provider.
|
||||
NewHashes(provider addrs.Provider, platform getproviders.Platform)
|
||||
|
||||
// ExistingHashes announces that all checksums were already tracked.
|
||||
ExistingHashes(provider addrs.Provider, platform getproviders.Platform)
|
||||
|
||||
// Success announces a successful completion, with whether any changes were made.
|
||||
Success(madeChanges bool)
|
||||
|
||||
// Diagnostics renders diagnostics.
|
||||
Diagnostics(diags tfdiags.Diagnostics)
|
||||
|
||||
// HelpPrompt directs the user to the help output.
|
||||
HelpPrompt()
|
||||
}
|
||||
|
||||
// NewProvidersLock returns an initialized ProvidersLock implementation.
|
||||
func NewProvidersLock(view *View) ProvidersLock {
|
||||
return &ProvidersLockHuman{view: view}
|
||||
}
|
||||
|
||||
// ProvidersLockHuman is the human-readable implementation of the ProvidersLock view.
|
||||
type ProvidersLockHuman struct {
|
||||
view *View
|
||||
}
|
||||
|
||||
var _ ProvidersLock = (*ProvidersLockHuman)(nil)
|
||||
|
||||
func (v *ProvidersLockHuman) Fetching(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform) {
|
||||
v.view.streams.Println(fmt.Sprintf("- Fetching %s %s for %s...", provider.ForDisplay(), version, platform))
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) FetchSuccess(provider addrs.Provider, version getproviders.Version, platform getproviders.Platform, auth string, keyID string) {
|
||||
if keyID != "" {
|
||||
keyID = v.view.colorize.Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID))
|
||||
}
|
||||
v.view.streams.Println(fmt.Sprintf("- Retrieved %s %s for %s (%s%s)", provider.ForDisplay(), version, platform, auth, keyID))
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) NewProvider(provider addrs.Provider, platform getproviders.Platform) {
|
||||
v.view.streams.Println(fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; This was a new provider and the checksums for this platform are now tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) NewHashes(provider addrs.Provider, platform getproviders.Platform) {
|
||||
v.view.streams.Println(fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; Additional checksums for this platform are now tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) ExistingHashes(provider addrs.Provider, platform getproviders.Platform) {
|
||||
v.view.streams.Println(fmt.Sprintf(
|
||||
"- Obtained %s checksums for %s; All checksums for this platform were already tracked in the lock file",
|
||||
provider.ForDisplay(),
|
||||
platform))
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) Success(madeChanges bool) {
|
||||
if madeChanges {
|
||||
v.view.streams.Println(v.view.colorize.Color("\n[bold][green]Success![reset] [bold]Terraform has updated the lock file.[reset]"))
|
||||
v.view.streams.Println("\nReview the changes in .terraform.lock.hcl and then commit to your\nversion control system to retain the new checksums.")
|
||||
} else {
|
||||
v.view.streams.Println(v.view.colorize.Color("\n[bold][green]Success![reset] [bold]Terraform has validated the lock file and found no need for changes.[reset]"))
|
||||
}
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) Diagnostics(diags tfdiags.Diagnostics) {
|
||||
v.view.Diagnostics(diags)
|
||||
}
|
||||
|
||||
func (v *ProvidersLockHuman) HelpPrompt() {
|
||||
v.view.HelpPrompt("providers lock")
|
||||
}
|
||||
141
internal/command/views/providers_lock_test.go
Normal file
141
internal/command/views/providers_lock_test.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/command/arguments"
|
||||
"github.com/hashicorp/terraform/internal/getproviders"
|
||||
"github.com/hashicorp/terraform/internal/terminal"
|
||||
)
|
||||
|
||||
func TestProvidersLockHuman_Fetching(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
version := getproviders.MustParseVersion("1.0.0")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.Fetching(provider, version, platform)
|
||||
|
||||
got := done(t).Stdout()
|
||||
want := "- Fetching hashicorp/test 1.0.0 for linux_amd64...\n"
|
||||
if got != want {
|
||||
t.Fatalf("wrong output\n got: %q\nwant: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_FetchSuccess(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
version := getproviders.MustParseVersion("1.0.0")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.FetchSuccess(provider, version, platform, "signed", "")
|
||||
|
||||
got := done(t).Stdout()
|
||||
want := "- Retrieved hashicorp/test 1.0.0 for linux_amd64 (signed)\n"
|
||||
if got != want {
|
||||
t.Fatalf("wrong output\n got: %q\nwant: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_FetchSuccess_withKeyID(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
version := getproviders.MustParseVersion("1.0.0")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.FetchSuccess(provider, version, platform, "signed", "ABC123")
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "ABC123") {
|
||||
t.Fatalf("expected output to contain key ID, got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_NewProvider(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.NewProvider(provider, platform)
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "new provider") {
|
||||
t.Fatalf("expected output to mention new provider, got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_NewHashes(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.NewHashes(provider, platform)
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "Additional checksums") {
|
||||
t.Fatalf("expected output to mention additional checksums, got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_ExistingHashes(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
|
||||
provider := addrs.NewDefaultProvider("test")
|
||||
platform := getproviders.Platform{OS: "linux", Arch: "amd64"}
|
||||
|
||||
v.ExistingHashes(provider, platform)
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "already tracked") {
|
||||
t.Fatalf("expected output to mention already tracked, got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_Success_withChanges(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
|
||||
|
||||
v.Success(true)
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "updated the lock file") {
|
||||
t.Fatalf("expected output to mention updated lock file, got: %q", got)
|
||||
}
|
||||
if !strings.Contains(got, "Review the changes") {
|
||||
t.Fatalf("expected output to mention reviewing changes, got: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvidersLockHuman_Success_noChanges(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := NewProvidersLock(NewView(streams))
|
||||
v.(*ProvidersLockHuman).view.Configure(&arguments.View{NoColor: true})
|
||||
|
||||
v.Success(false)
|
||||
|
||||
got := done(t).Stdout()
|
||||
if !strings.Contains(got, "no need for changes") {
|
||||
t.Fatalf("expected output to mention no need for changes, got: %q", got)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue