diff --git a/internal/cloud/backend.go b/internal/cloud/backend.go index b034d74963..0ad4328a48 100644 --- a/internal/cloud/backend.go +++ b/internal/cloud/backend.go @@ -296,7 +296,7 @@ func (b *Cloud) Configure(obj cty.Value) tfdiags.Diagnostics { // Get the token from the CLI Config File in the credentials section // if no token was set in the configuration if token == "" { - token, err = cliConfigToken(hostname, b.services) + token, err = CliConfigToken(hostname, b.services) if err != nil { diags = diags.Append(tfdiags.AttributeValue( tfdiags.Error, @@ -593,10 +593,10 @@ func resolveCloudConfig(obj cty.Value) (cloudConfig, tfdiags.Diagnostics) { return ret, diags } -// cliConfigToken returns the token for this host as configured in the credentials +// CliConfigToken returns the token for this host as configured in the credentials // section of the CLI Config File. If no token was configured, an empty // string will be returned instead. -func cliConfigToken(hostname svchost.Hostname, services *disco.Disco) (string, error) { +func CliConfigToken(hostname svchost.Hostname, services *disco.Disco) (string, error) { creds, err := services.CredentialsForHost(hostname) if err != nil { log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", hostname.ForDisplay(), err) diff --git a/internal/cloud/test.go b/internal/cloud/test.go index 3f97be0f8d..96703b777f 100644 --- a/internal/cloud/test.go +++ b/internal/cloud/test.go @@ -354,7 +354,7 @@ func (runner *TestSuiteRunner) client(addr tfaddr.Module, id tfe.RegistryModuleI return nil, nil, diags } - token, err := cliConfigToken(addr.Package.Host, runner.Services) + token, err := CliConfigToken(addr.Package.Host, runner.Services) if err != nil { diags = diags.Append(tfdiags.AttributeValue( tfdiags.Error, diff --git a/internal/command/stacks.go b/internal/command/stacks.go index 47e3b84788..5e7af5de5b 100644 --- a/internal/command/stacks.go +++ b/internal/command/stacks.go @@ -59,6 +59,8 @@ const ( // The stacks plugin release download service that the BinaryManager relies // on to fetch the plugin. stackspluginServiceID = "stacksplugin.v1" + + defaultHostname = "app.terraform.io" ) var ( @@ -165,20 +167,8 @@ func (c *StacksCommand) discoverAndConfigure() tfdiags.Diagnostics { displayHostname := os.Getenv("TF_STACKS_HOSTNAME") if strings.TrimSpace(displayHostname) == "" { - return diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "TF_STACKS_HOSTNAME is not set", - "TF_STACKS_HOSTNAME must be set to the hostname of the HCP Terraform instance", - )) - } - - token := os.Getenv("TF_STACKS_TOKEN") - if strings.TrimSpace(token) == "" { - return diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "TF_STACKS_TOKEN is not set", - "TF_STACKS_TOKEN must be set to the token of the HCP Terraform instance", - )) + log.Printf("[TRACE] stacksplugin hostname not set, falling back to %q", defaultHostname) + displayHostname = defaultHostname } hostname, err := svchost.ForComparison(displayHostname) @@ -208,6 +198,20 @@ func (c *StacksCommand) discoverAndConfigure() tfdiags.Diagnostics { // The discovery request worked, so cache the full results. cb.ServicesHost = host + token := os.Getenv("TF_STACKS_TOKEN") + if strings.TrimSpace(token) == "" { + // attempt to read from the credentials file + token, err = cloud.CliConfigToken(hostname, cb.Services()) + if err != nil { + // some commands like stacks init and validate could be run without a token so allow it without errors + diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Could not read token from credentials file, proceeding without a token", + err.Error(), + )) + } + } + // re-use the cached service discovery info for this TFC // instance to find our plugin service and TFE API URLs: pluginService, err := cb.ServicesHost.ServiceURL(stackspluginServiceID) @@ -244,6 +248,7 @@ func (c *StacksCommand) discoverAndConfigure() tfdiags.Diagnostics { OrganizationName: orgName, ProjectName: projectName, StackName: stackName, + TerminalWidth: c.Meta.Streams.Stdout.Columns(), } return diags @@ -346,6 +351,7 @@ type StacksPluginConfig struct { OrganizationName string `md:"tfc-organization"` ProjectName string `md:"tfc-project"` StackName string `md:"tfc-stack"` + TerminalWidth int `md:"terminal-width"` } func (c StacksPluginConfig) ToMetadata() metadata.MD { @@ -358,6 +364,7 @@ func (c StacksPluginConfig) ToMetadata() metadata.MD { "tfc-organization", c.OrganizationName, "tfc-project", c.ProjectName, "tfc-stack", c.StackName, + "terminal-width", fmt.Sprintf("%d", c.TerminalWidth), ) return md } diff --git a/internal/command/stacks_test.go b/internal/command/stacks_test.go index 39047376a7..f8b2019712 100644 --- a/internal/command/stacks_test.go +++ b/internal/command/stacks_test.go @@ -20,6 +20,7 @@ func TestStacksPluginConfig_ToMetadata(t *testing.T) { "tfc-project", "example-project", "tfc-stack", "example-stack", "terraform-binary-path", "", + "terminal-width", "78", ) inputStruct := StacksPluginConfig{ Address: "https://app.staging.terraform.io", @@ -30,6 +31,7 @@ func TestStacksPluginConfig_ToMetadata(t *testing.T) { ProjectName: "example-project", StackName: "example-stack", TerraformBinaryPath: "", + TerminalWidth: 78, } result := inputStruct.ToMetadata() if !reflect.DeepEqual(expected, result) {