github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/logout.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "strings" 7 8 svchost "github.com/hashicorp/terraform-svchost" 9 "github.com/hashicorp/terraform/internal/command/cliconfig" 10 "github.com/hashicorp/terraform/internal/tfdiags" 11 ) 12 13 // LogoutCommand is a Command implementation which removes stored credentials 14 // for a remote service host. 15 type LogoutCommand struct { 16 Meta 17 } 18 19 // Run implements cli.Command. 20 func (c *LogoutCommand) Run(args []string) int { 21 args = c.Meta.process(args) 22 cmdFlags := c.Meta.defaultFlagSet("logout") 23 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 24 if err := cmdFlags.Parse(args); err != nil { 25 return 1 26 } 27 28 args = cmdFlags.Args() 29 if len(args) > 1 { 30 c.Ui.Error( 31 "The logout command expects at most one argument: the host to log out of.") 32 cmdFlags.Usage() 33 return 1 34 } 35 36 var diags tfdiags.Diagnostics 37 38 givenHostname := "app.terraform.io" 39 if len(args) != 0 { 40 givenHostname = args[0] 41 } 42 43 hostname, err := svchost.ForComparison(givenHostname) 44 if err != nil { 45 diags = diags.Append(tfdiags.Sourceless( 46 tfdiags.Error, 47 "Invalid hostname", 48 fmt.Sprintf("The given hostname %q is not valid: %s.", givenHostname, err.Error()), 49 )) 50 c.showDiagnostics(diags) 51 return 1 52 } 53 54 // From now on, since we've validated the given hostname, we should use 55 // dispHostname in the UI to ensure we're presenting it in the canonical 56 // form, in case that helps users with debugging when things aren't 57 // working as expected. (Perhaps the normalization is part of the cause.) 58 dispHostname := hostname.ForDisplay() 59 60 creds := c.Services.CredentialsSource().(*cliconfig.CredentialsSource) 61 filename, _ := creds.CredentialsFilePath() 62 credsCtx := &loginCredentialsContext{ 63 Location: creds.HostCredentialsLocation(hostname), 64 LocalFilename: filename, // empty in the very unlikely event that we can't select a config directory for this user 65 HelperType: creds.CredentialsHelperType(), 66 } 67 68 if credsCtx.Location == cliconfig.CredentialsInOtherFile { 69 diags = diags.Append(tfdiags.Sourceless( 70 tfdiags.Error, 71 fmt.Sprintf("Credentials for %s are manually configured", dispHostname), 72 "The \"terraform logout\" command cannot log out because credentials for this host are manually configured in a CLI configuration file.\n\nTo log out, revoke the existing credentials and remove that block from the CLI configuration.", 73 )) 74 } 75 76 if diags.HasErrors() { 77 c.showDiagnostics(diags) 78 return 1 79 } 80 81 switch credsCtx.Location { 82 case cliconfig.CredentialsNotAvailable: 83 c.Ui.Output(fmt.Sprintf("No credentials for %s are stored.\n", dispHostname)) 84 return 0 85 case cliconfig.CredentialsViaHelper: 86 c.Ui.Output(fmt.Sprintf("Removing the stored credentials for %s from the configured\n%q credentials helper.\n", dispHostname, credsCtx.HelperType)) 87 case cliconfig.CredentialsInPrimaryFile: 88 c.Ui.Output(fmt.Sprintf("Removing the stored credentials for %s from the following file:\n %s\n", dispHostname, credsCtx.LocalFilename)) 89 } 90 91 err = creds.ForgetForHost(hostname) 92 if err != nil { 93 diags = diags.Append(tfdiags.Sourceless( 94 tfdiags.Error, 95 "Failed to remove API token", 96 fmt.Sprintf("Unable to remove stored API token: %s", err), 97 )) 98 } 99 100 c.showDiagnostics(diags) 101 if diags.HasErrors() { 102 return 1 103 } 104 105 c.Ui.Output( 106 fmt.Sprintf( 107 c.Colorize().Color(strings.TrimSpace(` 108 [green][bold]Success![reset] [bold]Terraform has removed the stored API token for %s.[reset] 109 `)), 110 dispHostname, 111 ) + "\n", 112 ) 113 114 return 0 115 } 116 117 // Help implements cli.Command. 118 func (c *LogoutCommand) Help() string { 119 defaultFile := c.defaultOutputFile() 120 if defaultFile == "" { 121 // Because this is just for the help message and it's very unlikely 122 // that a user wouldn't have a functioning home directory anyway, 123 // we'll just use a placeholder here. The real command has some 124 // more complex behavior for this case. This result is not correct 125 // on all platforms, but given how unlikely we are to hit this case 126 // that seems okay. 127 defaultFile = "~/.terraform/credentials.tfrc.json" 128 } 129 130 helpText := ` 131 Usage: terraform [global options] logout [hostname] 132 133 Removes locally-stored credentials for specified hostname. 134 135 Note: the API token is only removed from local storage, not destroyed on the 136 remote server, so it will remain valid until manually revoked. 137 138 If no hostname is provided, the default hostname is app.terraform.io. 139 %s 140 ` 141 return strings.TrimSpace(helpText) 142 } 143 144 // Synopsis implements cli.Command. 145 func (c *LogoutCommand) Synopsis() string { 146 return "Remove locally-stored credentials for a remote host" 147 } 148 149 func (c *LogoutCommand) defaultOutputFile() string { 150 if c.CLIConfigDir == "" { 151 return "" // no default available 152 } 153 return filepath.Join(c.CLIConfigDir, "credentials.tfrc.json") 154 }