command/login: Require "yes" to confirm
This is for consistency with other commands which use prompts, all of which require "yes" rather than "y" to confirm. We also migrate the login command to use UIInput, which now supports securely asking for passwords or secrets via the speakeasy library.
This commit is contained in:
parent
2d0b312ea8
commit
779fe37a1c
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/hashicorp/terraform-svchost/disco"
|
||||
"github.com/hashicorp/terraform/command/cliconfig"
|
||||
"github.com/hashicorp/terraform/httpclient"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
|
@ -453,12 +454,19 @@ func (c *LoginCommand) interactiveGetTokenByPassword(hostname svchost.Hostname,
|
|||
c.Ui.Output("\n---------------------------------------------------------------------------------\n")
|
||||
c.Ui.Output("Terraform must temporarily use your password to request an API token.\nThis password will NOT be saved locally.\n")
|
||||
|
||||
username, err := c.Ui.Ask(fmt.Sprintf("Username for %s:", hostname.ForDisplay()))
|
||||
username, err := c.UIInput().Input(context.Background(), &terraform.InputOpts{
|
||||
Id: "username",
|
||||
Query: fmt.Sprintf("Username for %s:", hostname.ForDisplay()),
|
||||
})
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("Failed to request username: %s", err))
|
||||
return nil, diags
|
||||
}
|
||||
password, err := c.Ui.AskSecret(fmt.Sprintf("Password for %s:", username))
|
||||
password, err := c.UIInput().Input(context.Background(), &terraform.InputOpts{
|
||||
Id: "password",
|
||||
Query: fmt.Sprintf("Password for %s:", hostname.ForDisplay()),
|
||||
Secret: true,
|
||||
})
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("Failed to request password: %s", err))
|
||||
return nil, diags
|
||||
|
@ -494,6 +502,8 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
|||
return "", diags
|
||||
}
|
||||
|
||||
c.Ui.Output("\n---------------------------------------------------------------------------------\n")
|
||||
|
||||
tokensURL := url.URL{
|
||||
Scheme: "https",
|
||||
Host: service.Hostname(),
|
||||
|
@ -508,6 +518,7 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
|||
c.Ui.Output(fmt.Sprintf("Terraform must now open a web browser to the tokens page for %s.\n", hostname.ForDisplay()))
|
||||
c.Ui.Output(fmt.Sprintf("If a browser does not open this automatically, open the following URL to proceed:\n %s\n", tokensURL.String()))
|
||||
} else {
|
||||
log.Printf("[DEBUG] error opening web browser: %s", err)
|
||||
// Assume we're on a platform where opening a browser isn't possible.
|
||||
launchBrowserManually = true
|
||||
}
|
||||
|
@ -533,7 +544,11 @@ func (c *LoginCommand) interactiveGetTokenByUI(hostname svchost.Hostname, credsC
|
|||
}
|
||||
}
|
||||
|
||||
token, err := c.Ui.AskSecret(fmt.Sprintf(c.Colorize().Color("Token for [bold]%s[reset]:"), hostname.ForDisplay()))
|
||||
token, err := c.UIInput().Input(context.Background(), &terraform.InputOpts{
|
||||
Id: "token",
|
||||
Query: fmt.Sprintf("Token for %s:", hostname.ForDisplay()),
|
||||
Secret: true,
|
||||
})
|
||||
if err != nil {
|
||||
diags := diags.Append(fmt.Errorf("Failed to retrieve token: %s", err))
|
||||
return "", diags
|
||||
|
@ -590,7 +605,11 @@ func (c *LoginCommand) interactiveContextConsent(hostname svchost.Hostname, gran
|
|||
}
|
||||
}
|
||||
|
||||
v, err := c.Ui.Ask("Do you want to proceed? (y/n)")
|
||||
v, err := c.UIInput().Input(context.Background(), &terraform.InputOpts{
|
||||
Id: "approve",
|
||||
Query: "Do you want to proceed?",
|
||||
Description: `Only 'yes' will be accepted to confirm.`,
|
||||
})
|
||||
if err != nil {
|
||||
// Should not happen because this command checks that input is enabled
|
||||
// before we get to this point.
|
||||
|
@ -598,12 +617,7 @@ func (c *LoginCommand) interactiveContextConsent(hostname svchost.Hostname, gran
|
|||
return false, diags
|
||||
}
|
||||
|
||||
switch strings.ToLower(v) {
|
||||
case "y", "yes":
|
||||
return true, diags
|
||||
default:
|
||||
return false, diags
|
||||
}
|
||||
return strings.ToLower(v) == "yes", diags
|
||||
}
|
||||
|
||||
func (c *LoginCommand) listenerForCallback(minPort, maxPort uint16) (net.Listener, string, error) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
|
@ -34,7 +33,7 @@ func TestLogin(t *testing.T) {
|
|||
ts := httptest.NewServer(tfeserver.Handler)
|
||||
defer ts.Close()
|
||||
|
||||
loginTestCase := func(test func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string))) func(t *testing.T) {
|
||||
loginTestCase := func(test func(t *testing.T, c *LoginCommand, ui *cli.MockUi)) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
workDir, err := ioutil.TempDir("", "terraform-test-command-login")
|
||||
|
@ -48,15 +47,15 @@ func TestLogin(t *testing.T) {
|
|||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
// Do not use the NewMockUi initializer here, as we want to delay
|
||||
// the call to init until after setting up the input mocks
|
||||
ui := new(cli.MockUi)
|
||||
|
||||
browserLauncher := webbrowser.NewMockLauncher(ctx)
|
||||
creds := cliconfig.EmptyCredentialsSourceForTests(filepath.Join(workDir, "credentials.tfrc.json"))
|
||||
svcs := disco.NewWithCredentialsSource(creds)
|
||||
svcs.SetUserAgent(httpclient.TerraformUserAgent(version.String()))
|
||||
|
||||
inputBuf := &bytes.Buffer{}
|
||||
ui.InputReader = inputBuf
|
||||
|
||||
svcs.ForceHostServices(svchost.Hostname("app.terraform.io"), map[string]interface{}{
|
||||
"login.v1": map[string]interface{}{
|
||||
// On app.terraform.io we use password-based authorization.
|
||||
|
@ -96,16 +95,16 @@ func TestLogin(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
test(t, c, ui, func(data string) {
|
||||
t.Helper()
|
||||
inputBuf.WriteString(data)
|
||||
})
|
||||
test(t, c, ui)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("defaulting to app.terraform.io with password flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string)) {
|
||||
// Enter "yes" at the consent prompt, then a username and then a password.
|
||||
inp("yes\nfoo\nbar\n")
|
||||
t.Run("defaulting to app.terraform.io with password flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "yes",
|
||||
"username": "foo",
|
||||
"password": "bar",
|
||||
})()
|
||||
status := c.Run(nil)
|
||||
if status != 0 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
|
@ -121,9 +120,11 @@ func TestLogin(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
|
||||
t.Run("example.com with authorization code flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string)) {
|
||||
t.Run("example.com with authorization code flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
// Enter "yes" at the consent prompt.
|
||||
inp("yes\n")
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "yes",
|
||||
})()
|
||||
status := c.Run([]string{"example.com"})
|
||||
if status != 0 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
|
@ -139,10 +140,13 @@ func TestLogin(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
|
||||
t.Run("TFE host without login support", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string)) {
|
||||
t.Run("TFE host without login support", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
// Enter "yes" at the consent prompt, then paste a token with some
|
||||
// accidental whitespace.
|
||||
inp("yes\n good-token \n")
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "yes",
|
||||
"token": " good-token ",
|
||||
})()
|
||||
status := c.Run([]string{"tfe.acme.com"})
|
||||
if status != 0 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
|
@ -158,9 +162,12 @@ func TestLogin(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
|
||||
t.Run("TFE host without login support, incorrectly pasted token", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string)) {
|
||||
t.Run("TFE host without login support, incorrectly pasted token", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
// Enter "yes" at the consent prompt, then paste an invalid token.
|
||||
inp("yes\ngood-tok\n")
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "yes",
|
||||
"token": "good-tok",
|
||||
})()
|
||||
status := c.Run([]string{"tfe.acme.com"})
|
||||
if status != 1 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
|
@ -176,7 +183,7 @@ func TestLogin(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
|
||||
t.Run("host without login or TFE API support", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi, inp func(string)) {
|
||||
t.Run("host without login or TFE API support", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
status := c.Run([]string{"unsupported.example.net"})
|
||||
if status == 0 {
|
||||
t.Fatalf("successful exit; want error")
|
||||
|
@ -186,4 +193,34 @@ func TestLogin(t *testing.T) {
|
|||
t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
|
||||
}
|
||||
}))
|
||||
|
||||
t.Run("answering no cancels", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
// Enter "no" at the consent prompt
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "no",
|
||||
})()
|
||||
status := c.Run(nil)
|
||||
if status != 1 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
if got, want := ui.ErrorWriter.String(), "Login cancelled"; !strings.Contains(got, want) {
|
||||
t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
|
||||
}
|
||||
}))
|
||||
|
||||
t.Run("answering y cancels", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) {
|
||||
// Enter "y" at the consent prompt
|
||||
defer testInputMap(t, map[string]string{
|
||||
"approve": "y",
|
||||
})()
|
||||
status := c.Run(nil)
|
||||
if status != 1 {
|
||||
t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
if got, want := ui.ErrorWriter.String(), "Login cancelled"; !strings.Contains(got, want) {
|
||||
t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ import (
|
|||
"sync/atomic"
|
||||
"unicode"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/mitchellh/colorstring"
|
||||
)
|
||||
|
||||
|
@ -128,8 +130,14 @@ func (i *UIInput) Input(ctx context.Context, opts *terraform.InputOpts) (string,
|
|||
}
|
||||
defer atomic.CompareAndSwapInt32(&i.listening, 1, 0)
|
||||
|
||||
var line string
|
||||
var err error
|
||||
if opts.Secret && isatty.IsTerminal(os.Stdin.Fd()) {
|
||||
line, err = speakeasy.Ask("")
|
||||
} else {
|
||||
buf := bufio.NewReader(r)
|
||||
line, err := buf.ReadString('\n')
|
||||
line, err = buf.ReadString('\n')
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[ERR] UIInput scan err: %s", err)
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -21,6 +21,7 @@ require (
|
|||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.31.9
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/bmatcuk/doublestar v1.1.5
|
||||
github.com/boltdb/bolt v1.3.1 // indirect
|
||||
|
@ -90,6 +91,7 @@ require (
|
|||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88
|
||||
github.com/mattn/go-colorable v0.1.1
|
||||
github.com/mattn/go-isatty v0.0.5
|
||||
github.com/mattn/go-shellwords v1.0.4
|
||||
github.com/miekg/dns v1.0.8 // indirect
|
||||
github.com/mitchellh/cli v1.0.0
|
||||
|
|
10
go.sum
10
go.sum
|
@ -363,8 +363,6 @@ github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAly
|
|||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b h1:/1RFh2SLCJ+tEnT73+Fh5R2AO89sQqs8ba7o+hx1G0Y=
|
||||
github.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b/go.mod h1:wr1VqkwW0AB5JS0QLy5GpVMS9E3VtRoSYXUYyVk46KY=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88 h1:cxuVcCvCLD9yYDbRCWw0jSgh1oT6P6mv3aJDKK5o7X4=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
|
@ -436,8 +434,6 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58 h1:m3CEgv3ah1Rhy82L+c0QG/U3VyY1UsvsIdkh0/rU97Y=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db h1:9uViuKtx1jrlXLBW/pMnhOfzn3iSEdLase/But/IZRU=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||
|
@ -549,8 +545,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -587,8 +581,6 @@ golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -623,8 +615,6 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
|
@ -25,4 +25,8 @@ type InputOpts struct {
|
|||
|
||||
// Default will be the value returned if no data is entered.
|
||||
Default string
|
||||
|
||||
// Secret should be true if we are asking for sensitive input.
|
||||
// If attached to a TTY, Terraform will disable echo.
|
||||
Secret bool
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ github.com/aws/aws-sdk-go/service/sts/stsiface
|
|||
# github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
|
||||
github.com/bgentry/go-netrc/netrc
|
||||
# github.com/bgentry/speakeasy v0.1.0
|
||||
## explicit
|
||||
github.com/bgentry/speakeasy
|
||||
# github.com/blang/semver v3.5.1+incompatible
|
||||
## explicit
|
||||
|
@ -476,6 +477,7 @@ github.com/masterzen/winrm/soap
|
|||
## explicit
|
||||
github.com/mattn/go-colorable
|
||||
# github.com/mattn/go-isatty v0.0.5
|
||||
## explicit
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/mattn/go-shellwords v1.0.4
|
||||
## explicit
|
||||
|
|
Loading…
Reference in New Issue