... | ... |
@@ -78,10 +78,11 @@ func NewCommandCLI(name, fullName string, in io.Reader, out, errout io.Writer) * |
78 | 78 |
Short: "Command line tools for managing applications", |
79 | 79 |
Long: cliLong, |
80 | 80 |
Run: func(c *cobra.Command, args []string) { |
81 |
- c.SetOutput(out) |
|
81 |
+ explainOut := term.NewResponsiveWriter(out) |
|
82 |
+ c.SetOutput(explainOut) |
|
82 | 83 |
cmdutil.RequireNoArguments(c, args) |
83 |
- fmt.Fprint(out, cliLong) |
|
84 |
- fmt.Fprintf(out, cliExplain, fullName) |
|
84 |
+ fmt.Fprint(explainOut, cliLong) |
|
85 |
+ fmt.Fprintf(explainOut, cliExplain, fullName) |
|
85 | 86 |
}, |
86 | 87 |
BashCompletionFunction: bashCompletionFunc, |
87 | 88 |
} |
... | ... |
@@ -10,17 +10,17 @@ import ( |
10 | 10 |
"net/url" |
11 | 11 |
"os" |
12 | 12 |
|
13 |
- cmdutil "github.com/openshift/origin/pkg/cmd/util" |
|
13 |
+ "github.com/openshift/origin/pkg/client" |
|
14 |
+ "github.com/openshift/origin/pkg/user/api" |
|
15 |
+ clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" |
|
16 |
+ |
|
17 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
14 | 18 |
kapi "k8s.io/kubernetes/pkg/api" |
15 | 19 |
kerrors "k8s.io/kubernetes/pkg/api/errors" |
16 | 20 |
"k8s.io/kubernetes/pkg/client/restclient" |
17 |
- clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" |
|
18 | 21 |
kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" |
19 | 22 |
"k8s.io/kubernetes/pkg/util/sets" |
20 |
- "k8s.io/kubernetes/pkg/util/term" |
|
21 |
- |
|
22 |
- "github.com/openshift/origin/pkg/client" |
|
23 |
- "github.com/openshift/origin/pkg/user/api" |
|
23 |
+ kterm "k8s.io/kubernetes/pkg/util/term" |
|
24 | 24 |
) |
25 | 25 |
|
26 | 26 |
// getMatchingClusters examines the kubeconfig for all clusters that point to the same server |
... | ... |
@@ -96,12 +96,12 @@ func promptForInsecureTLS(reader io.Reader, out io.Writer, reason error) bool { |
96 | 96 |
} |
97 | 97 |
} |
98 | 98 |
var input bool |
99 |
- if term.IsTerminal(reader) { |
|
99 |
+ if kterm.IsTerminal(reader) { |
|
100 | 100 |
if len(insecureTLSRequestReason) > 0 { |
101 | 101 |
fmt.Fprintln(out, insecureTLSRequestReason) |
102 | 102 |
} |
103 | 103 |
fmt.Fprintln(out, "You can bypass the certificate check, but any data you send to the server could be intercepted by others.") |
104 |
- input = cmdutil.PromptForBool(os.Stdin, out, "Use insecure connections? (y/n): ") |
|
104 |
+ input = term.PromptForBool(os.Stdin, out, "Use insecure connections? (y/n): ") |
|
105 | 105 |
fmt.Fprintln(out) |
106 | 106 |
} |
107 | 107 |
return input |
... | ... |
@@ -17,7 +17,7 @@ import ( |
17 | 17 |
kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" |
18 | 18 |
kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" |
19 | 19 |
"k8s.io/kubernetes/pkg/util/sets" |
20 |
- "k8s.io/kubernetes/pkg/util/term" |
|
20 |
+ kterm "k8s.io/kubernetes/pkg/util/term" |
|
21 | 21 |
|
22 | 22 |
"github.com/openshift/origin/pkg/client" |
23 | 23 |
"github.com/openshift/origin/pkg/cmd/cli/cmd/errors" |
... | ... |
@@ -25,6 +25,7 @@ import ( |
25 | 25 |
cmderr "github.com/openshift/origin/pkg/cmd/errors" |
26 | 26 |
cmdutil "github.com/openshift/origin/pkg/cmd/util" |
27 | 27 |
"github.com/openshift/origin/pkg/cmd/util/clientcmd" |
28 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
28 | 29 |
"github.com/openshift/origin/pkg/cmd/util/tokencmd" |
29 | 30 |
"github.com/openshift/origin/pkg/user/api" |
30 | 31 |
) |
... | ... |
@@ -86,11 +87,11 @@ func (o *LoginOptions) getClientConfig() (*restclient.Config, error) { |
86 | 86 |
|
87 | 87 |
if len(o.Server) == 0 { |
88 | 88 |
// we need to have a server to talk to |
89 |
- if term.IsTerminal(o.Reader) { |
|
89 |
+ if kterm.IsTerminal(o.Reader) { |
|
90 | 90 |
for !o.serverProvided() { |
91 | 91 |
defaultServer := defaultClusterURL |
92 | 92 |
promptMsg := fmt.Sprintf("Server [%s]: ", defaultServer) |
93 |
- o.Server = cmdutil.PromptForStringWithDefault(o.Reader, o.Out, defaultServer, promptMsg) |
|
93 |
+ o.Server = term.PromptForStringWithDefault(o.Reader, o.Out, defaultServer, promptMsg) |
|
94 | 94 |
} |
95 | 95 |
} |
96 | 96 |
} |
... | ... |
@@ -12,6 +12,7 @@ import ( |
12 | 12 |
|
13 | 13 |
"github.com/openshift/origin/pkg/client" |
14 | 14 |
cliconfig "github.com/openshift/origin/pkg/cmd/cli/config" |
15 |
+ "github.com/openshift/origin/pkg/cmd/templates" |
|
15 | 16 |
"github.com/openshift/origin/pkg/cmd/util/clientcmd" |
16 | 17 |
projectapi "github.com/openshift/origin/pkg/project/api" |
17 | 18 |
) |
... | ... |
@@ -37,20 +38,21 @@ type NewProjectOptions struct { |
37 | 37 |
} |
38 | 38 |
|
39 | 39 |
// RequestProject command description. |
40 |
-const ( |
|
41 |
- requestProjectLong = ` |
|
42 |
-Create a new project for yourself |
|
40 |
+var ( |
|
41 |
+ requestProjectLong = templates.LongDesc(` |
|
42 |
+ Create a new project for yourself |
|
43 | 43 |
|
44 |
-If your administrator allows self-service, this command will create a new project for you and assign you |
|
45 |
-as the project admin. |
|
44 |
+ If your administrator allows self-service, this command will create a new project for you and assign you |
|
45 |
+ as the project admin. |
|
46 | 46 |
|
47 |
-After your project is created it will become the default project in your config.` |
|
47 |
+ After your project is created it will become the default project in your config.`) |
|
48 | 48 |
|
49 |
- requestProjectExample = ` # Create a new project with minimal information |
|
50 |
- %[1]s %[2]s web-team-dev |
|
49 |
+ requestProjectExample = templates.Examples(` |
|
50 |
+ # Create a new project with minimal information |
|
51 |
+ %[1]s %[2]s web-team-dev |
|
51 | 52 |
|
52 |
- # Create a new project with a display name and description |
|
53 |
- %[1]s %[2]s web-team-dev --display-name="Web Team Development" --description="Development project for the web team."` |
|
53 |
+ # Create a new project with a display name and description |
|
54 |
+ %[1]s %[2]s web-team-dev --display-name="Web Team Development" --description="Development project for the web team."`) |
|
54 | 55 |
) |
55 | 56 |
|
56 | 57 |
// RequestProject next steps. |
... | ... |
@@ -71,7 +73,7 @@ To switch to this project and start adding applications, use: |
71 | 71 |
) |
72 | 72 |
|
73 | 73 |
// NewCmdRequestProject implement the OpenShift cli RequestProject command. |
74 |
-func NewCmdRequestProject(name, baseName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { |
|
74 |
+func NewCmdRequestProject(name, baseName string, f *clientcmd.Factory, out, errout io.Writer) *cobra.Command { |
|
75 | 75 |
o := &NewProjectOptions{} |
76 | 76 |
o.Out = out |
77 | 77 |
o.Name = baseName |
... | ... |
@@ -12,8 +12,8 @@ import ( |
12 | 12 |
"k8s.io/kubernetes/pkg/client/unversioned" |
13 | 13 |
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
14 | 14 |
|
15 |
- "github.com/openshift/origin/pkg/cmd/util" |
|
16 | 15 |
"github.com/openshift/origin/pkg/cmd/util/clientcmd" |
16 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
17 | 17 |
"github.com/openshift/origin/pkg/serviceaccounts" |
18 | 18 |
) |
19 | 19 |
|
... | ... |
@@ -128,7 +128,7 @@ func (o *GetServiceAccountTokenOptions) Run() error { |
128 | 128 |
} |
129 | 129 |
|
130 | 130 |
fmt.Fprintf(o.Out, string(token)) |
131 |
- if util.IsTerminalWriter(o.Out) { |
|
131 |
+ if term.IsTerminalWriter(o.Out) { |
|
132 | 132 |
// pretty-print for a TTY |
133 | 133 |
fmt.Fprintf(o.Out, "\n") |
134 | 134 |
} |
... | ... |
@@ -17,8 +17,8 @@ import ( |
17 | 17 |
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
18 | 18 |
"k8s.io/kubernetes/pkg/watch" |
19 | 19 |
|
20 |
- "github.com/openshift/origin/pkg/cmd/util" |
|
21 | 20 |
"github.com/openshift/origin/pkg/cmd/util/clientcmd" |
21 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
22 | 22 |
"github.com/openshift/origin/pkg/serviceaccounts" |
23 | 23 |
osautil "github.com/openshift/origin/pkg/serviceaccounts/util" |
24 | 24 |
) |
... | ... |
@@ -176,7 +176,7 @@ func (o *NewServiceAccountTokenOptions) Run() error { |
176 | 176 |
} |
177 | 177 |
|
178 | 178 |
fmt.Fprintf(o.Out, string(token)) |
179 |
- if util.IsTerminalWriter(o.Out) { |
|
179 |
+ if term.IsTerminalWriter(o.Out) { |
|
180 | 180 |
// pretty-print for a TTY |
181 | 181 |
fmt.Fprintf(o.Out, "\n") |
182 | 182 |
} |
... | ... |
@@ -11,9 +11,9 @@ import ( |
11 | 11 |
"k8s.io/kubernetes/pkg/api" |
12 | 12 |
client "k8s.io/kubernetes/pkg/client/unversioned" |
13 | 13 |
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
14 |
- "k8s.io/kubernetes/pkg/util/term" |
|
14 |
+ kterm "k8s.io/kubernetes/pkg/util/term" |
|
15 | 15 |
|
16 |
- cmdutil "github.com/openshift/origin/pkg/cmd/util" |
|
16 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
17 | 17 |
) |
18 | 18 |
|
19 | 19 |
const ( |
... | ... |
@@ -166,11 +166,11 @@ func (o *CreateBasicAuthSecretOptions) Complete(f *kcmdutil.Factory, args []stri |
166 | 166 |
if len(o.Password) != 0 { |
167 | 167 |
return errors.New("must provide either --prompt or --password flag") |
168 | 168 |
} |
169 |
- if !term.IsTerminal(o.Reader) { |
|
169 |
+ if !kterm.IsTerminal(o.Reader) { |
|
170 | 170 |
return errors.New("provided reader is not a terminal") |
171 | 171 |
} |
172 | 172 |
|
173 |
- o.Password = cmdutil.PromptForPasswordString(o.Reader, o.Out, "Password: ") |
|
173 |
+ o.Password = term.PromptForPasswordString(o.Reader, o.Out, "Password: ") |
|
174 | 174 |
if len(o.Password) == 0 { |
175 | 175 |
return errors.New("password must be provided") |
176 | 176 |
} |
... | ... |
@@ -8,7 +8,7 @@ import ( |
8 | 8 |
"io/ioutil" |
9 | 9 |
"os" |
10 | 10 |
|
11 |
- "github.com/openshift/origin/pkg/cmd/util" |
|
11 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
12 | 12 |
"github.com/spf13/cobra" |
13 | 13 |
|
14 | 14 |
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
... | ... |
@@ -38,7 +38,7 @@ type DecryptOptions struct { |
38 | 38 |
|
39 | 39 |
const decryptExample = ` # Decrypt an encrypted file to a cleartext file: |
40 | 40 |
%[1]s --key=secret.key --in=secret.encrypted --out=secret.decrypted |
41 |
- |
|
41 |
+ |
|
42 | 42 |
# Decrypt from stdin to stdout: |
43 | 43 |
%[1]s --key=secret.key < secret2.encrypted > secret2.decrypted |
44 | 44 |
` |
... | ... |
@@ -79,7 +79,7 @@ func (o *DecryptOptions) Validate(args []string) error { |
79 | 79 |
return errors.New("no arguments are supported") |
80 | 80 |
} |
81 | 81 |
|
82 |
- if len(o.EncryptedFile) == 0 && len(o.EncryptedData) == 0 && (o.EncryptedReader == nil || util.IsTerminalReader(o.EncryptedReader)) { |
|
82 |
+ if len(o.EncryptedFile) == 0 && len(o.EncryptedData) == 0 && (o.EncryptedReader == nil || term.IsTerminalReader(o.EncryptedReader)) { |
|
83 | 83 |
return errors.New("no input data specified") |
84 | 84 |
} |
85 | 85 |
if len(o.EncryptedFile) > 0 && len(o.EncryptedData) > 0 { |
... | ... |
@@ -105,7 +105,7 @@ func (o *DecryptOptions) Decrypt() error { |
105 | 105 |
} |
106 | 106 |
case len(o.EncryptedData) > 0: |
107 | 107 |
data = o.EncryptedData |
108 |
- case o.EncryptedReader != nil && !util.IsTerminalReader(o.EncryptedReader): |
|
108 |
+ case o.EncryptedReader != nil && !term.IsTerminalReader(o.EncryptedReader): |
|
109 | 109 |
if d, err := ioutil.ReadAll(o.EncryptedReader); err != nil { |
110 | 110 |
return err |
111 | 111 |
} else { |
... | ... |
@@ -147,7 +147,7 @@ func (o *DecryptOptions) Decrypt() error { |
147 | 147 |
} |
148 | 148 |
case o.DecryptedWriter != nil: |
149 | 149 |
fmt.Fprint(o.DecryptedWriter, string(plaintext)) |
150 |
- if util.IsTerminalWriter(o.DecryptedWriter) { |
|
150 |
+ if term.IsTerminalWriter(o.DecryptedWriter) { |
|
151 | 151 |
fmt.Fprintln(o.DecryptedWriter) |
152 | 152 |
} |
153 | 153 |
} |
... | ... |
@@ -12,12 +12,12 @@ import ( |
12 | 12 |
"unicode" |
13 | 13 |
"unicode/utf8" |
14 | 14 |
|
15 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
15 | 16 |
"github.com/spf13/cobra" |
16 | 17 |
|
17 | 18 |
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
18 | 19 |
|
19 | 20 |
configapi "github.com/openshift/origin/pkg/cmd/server/api" |
20 |
- "github.com/openshift/origin/pkg/cmd/util" |
|
21 | 21 |
pemutil "github.com/openshift/origin/pkg/cmd/util/pem" |
22 | 22 |
) |
23 | 23 |
|
... | ... |
@@ -47,7 +47,7 @@ type EncryptOptions struct { |
47 | 47 |
|
48 | 48 |
const encryptExample = ` # Encrypt the content of secret.txt with a generated key: |
49 | 49 |
%[1]s --genkey=secret.key --in=secret.txt --out=secret.encrypted |
50 |
- |
|
50 |
+ |
|
51 | 51 |
# Encrypt the content of secret2.txt with an existing key: |
52 | 52 |
%[1]s --key=secret.key < secret2.txt > secret2.encrypted |
53 | 53 |
` |
... | ... |
@@ -127,9 +127,9 @@ func (o *EncryptOptions) Encrypt() error { |
127 | 127 |
// Don't warn in cases where we're explicitly being given the data to use |
128 | 128 |
warnWhitespace = false |
129 | 129 |
data = o.CleartextData |
130 |
- case o.CleartextReader != nil && util.IsTerminalReader(o.CleartextReader) && o.PromptWriter != nil: |
|
130 |
+ case o.CleartextReader != nil && term.IsTerminalReader(o.CleartextReader) && o.PromptWriter != nil: |
|
131 | 131 |
// Read a single line from stdin with prompting |
132 |
- data = []byte(util.PromptForString(o.CleartextReader, o.PromptWriter, "Data to encrypt: ")) |
|
132 |
+ data = []byte(term.PromptForString(o.CleartextReader, o.PromptWriter, "Data to encrypt: ")) |
|
133 | 133 |
case o.CleartextReader != nil: |
134 | 134 |
// Read data from stdin without prompting (allows binary data and piping) |
135 | 135 |
if d, err := ioutil.ReadAll(o.CleartextReader); err != nil { |
... | ... |
@@ -7,6 +7,7 @@ import ( |
7 | 7 |
"text/template" |
8 | 8 |
"unicode" |
9 | 9 |
|
10 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
10 | 11 |
"github.com/spf13/cobra" |
11 | 12 |
flag "github.com/spf13/pflag" |
12 | 13 |
) |
... | ... |
@@ -81,27 +82,30 @@ func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGr |
81 | 81 |
if cmd == nil { |
82 | 82 |
panic("nil root command") |
83 | 83 |
} |
84 |
- cmd.SetHelpTemplate(MainHelpTemplate()) |
|
85 | 84 |
templater := &templater{ |
86 | 85 |
RootCmd: cmd, |
87 | 86 |
UsageTemplate: MainUsageTemplate(), |
87 |
+ HelpTemplate: MainHelpTemplate(), |
|
88 | 88 |
CommandGroups: groups, |
89 | 89 |
Filtered: filters, |
90 | 90 |
} |
91 | 91 |
cmd.SetUsageFunc(templater.UsageFunc()) |
92 |
+ cmd.SetHelpFunc(templater.HelpFunc()) |
|
92 | 93 |
return templater |
93 | 94 |
} |
94 | 95 |
|
95 | 96 |
func UseOptionsTemplates(cmd *cobra.Command) { |
96 |
- cmd.SetHelpTemplate(OptionsHelpTemplate()) |
|
97 | 97 |
templater := &templater{ |
98 | 98 |
UsageTemplate: OptionsUsageTemplate(), |
99 |
+ HelpTemplate: OptionsHelpTemplate(), |
|
99 | 100 |
} |
100 | 101 |
cmd.SetUsageFunc(templater.UsageFunc()) |
102 |
+ cmd.SetHelpFunc(templater.HelpFunc()) |
|
101 | 103 |
} |
102 | 104 |
|
103 | 105 |
type templater struct { |
104 | 106 |
UsageTemplate string |
107 |
+ HelpTemplate string |
|
105 | 108 |
RootCmd *cobra.Command |
106 | 109 |
CommandGroups |
107 | 110 |
Filtered []string |
... | ... |
@@ -112,39 +116,55 @@ func (templater *templater) ExposeFlags(cmd *cobra.Command, flags ...string) Fla |
112 | 112 |
return templater |
113 | 113 |
} |
114 | 114 |
|
115 |
+func (templater *templater) HelpFunc() func(*cobra.Command, []string) { |
|
116 |
+ return func(c *cobra.Command, s []string) { |
|
117 |
+ t := template.New("help") |
|
118 |
+ t.Funcs(templater.templateFuncs()) |
|
119 |
+ template.Must(t.Parse(templater.HelpTemplate)) |
|
120 |
+ out := term.NewResponsiveWriter(c.OutOrStdout()) |
|
121 |
+ err := t.Execute(out, c) |
|
122 |
+ if err != nil { |
|
123 |
+ c.Println(err) |
|
124 |
+ } |
|
125 |
+ } |
|
126 |
+} |
|
127 |
+ |
|
115 | 128 |
func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error { |
116 | 129 |
return func(c *cobra.Command) error { |
117 |
- t := template.New("custom") |
|
118 |
- |
|
119 |
- t.Funcs(template.FuncMap{ |
|
120 |
- "trim": strings.TrimSpace, |
|
121 |
- "trimRight": func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) }, |
|
122 |
- "gt": cobra.Gt, |
|
123 |
- "eq": cobra.Eq, |
|
124 |
- "rpad": rpad, |
|
125 |
- "flagsNotIntersected": flagsNotIntersected, |
|
126 |
- "visibleFlags": visibleFlags, |
|
127 |
- "flagsUsages": flagsUsages, |
|
128 |
- "cmdGroups": templater.cmdGroups, |
|
129 |
- "rootCmd": templater.rootCmdName, |
|
130 |
- "isRootCmd": templater.isRootCmd, |
|
131 |
- "optionsCmdFor": templater.optionsCmdFor, |
|
132 |
- "usageLine": templater.usageLine, |
|
133 |
- "exposed": func(c *cobra.Command) *flag.FlagSet { |
|
134 |
- exposed := flag.NewFlagSet("exposed", flag.ContinueOnError) |
|
135 |
- if len(exposedFlags) > 0 { |
|
136 |
- for _, name := range exposedFlags { |
|
137 |
- if flag := c.Flags().Lookup(name); flag != nil { |
|
138 |
- exposed.AddFlag(flag) |
|
139 |
- } |
|
130 |
+ t := template.New("usage") |
|
131 |
+ t.Funcs(templater.templateFuncs(exposedFlags...)) |
|
132 |
+ template.Must(t.Parse(templater.UsageTemplate)) |
|
133 |
+ out := term.NewResponsiveWriter(c.OutOrStderr()) |
|
134 |
+ return t.Execute(out, c) |
|
135 |
+ } |
|
136 |
+} |
|
137 |
+ |
|
138 |
+func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncMap { |
|
139 |
+ return template.FuncMap{ |
|
140 |
+ "trim": strings.TrimSpace, |
|
141 |
+ "trimRight": func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) }, |
|
142 |
+ "gt": cobra.Gt, |
|
143 |
+ "eq": cobra.Eq, |
|
144 |
+ "rpad": rpad, |
|
145 |
+ "flagsNotIntersected": flagsNotIntersected, |
|
146 |
+ "visibleFlags": visibleFlags, |
|
147 |
+ "flagsUsages": flagsUsages, |
|
148 |
+ "cmdGroups": templater.cmdGroups, |
|
149 |
+ "rootCmd": templater.rootCmdName, |
|
150 |
+ "isRootCmd": templater.isRootCmd, |
|
151 |
+ "optionsCmdFor": templater.optionsCmdFor, |
|
152 |
+ "usageLine": templater.usageLine, |
|
153 |
+ "exposed": func(c *cobra.Command) *flag.FlagSet { |
|
154 |
+ exposed := flag.NewFlagSet("exposed", flag.ContinueOnError) |
|
155 |
+ if len(exposedFlags) > 0 { |
|
156 |
+ for _, name := range exposedFlags { |
|
157 |
+ if flag := c.Flags().Lookup(name); flag != nil { |
|
158 |
+ exposed.AddFlag(flag) |
|
140 | 159 |
} |
141 | 160 |
} |
142 |
- return exposed |
|
143 |
- }, |
|
144 |
- }) |
|
145 |
- |
|
146 |
- template.Must(t.Parse(templater.UsageTemplate)) |
|
147 |
- return t.Execute(c.OutOrStderr(), c) |
|
161 |
+ } |
|
162 |
+ return exposed |
|
163 |
+ }, |
|
148 | 164 |
} |
149 | 165 |
} |
150 | 166 |
|
151 | 167 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,122 @@ |
0 |
+package term |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bufio" |
|
4 |
+ "fmt" |
|
5 |
+ "io" |
|
6 |
+ "os" |
|
7 |
+ "strings" |
|
8 |
+ |
|
9 |
+ "github.com/docker/docker/pkg/term" |
|
10 |
+ "github.com/golang/glog" |
|
11 |
+ |
|
12 |
+ kterm "k8s.io/kubernetes/pkg/util/term" |
|
13 |
+) |
|
14 |
+ |
|
15 |
+// PromptForString takes an io.Reader and prompts for user input if it's a terminal, returning the result. |
|
16 |
+func PromptForString(r io.Reader, w io.Writer, format string, a ...interface{}) string { |
|
17 |
+ if w == nil { |
|
18 |
+ w = os.Stdout |
|
19 |
+ } |
|
20 |
+ |
|
21 |
+ fmt.Fprintf(w, format, a...) |
|
22 |
+ return readInput(r) |
|
23 |
+} |
|
24 |
+ |
|
25 |
+// PromptForPasswordString prompts for user input by disabling echo in terminal, useful for password prompt. |
|
26 |
+func PromptForPasswordString(r io.Reader, w io.Writer, format string, a ...interface{}) string { |
|
27 |
+ if w == nil { |
|
28 |
+ w = os.Stdout |
|
29 |
+ } |
|
30 |
+ |
|
31 |
+ if file, ok := r.(*os.File); ok { |
|
32 |
+ inFd := file.Fd() |
|
33 |
+ |
|
34 |
+ if term.IsTerminal(inFd) { |
|
35 |
+ oldState, err := term.SaveState(inFd) |
|
36 |
+ if err != nil { |
|
37 |
+ glog.V(3).Infof("Unable to save terminal state") |
|
38 |
+ return PromptForString(r, w, format, a...) |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ fmt.Fprintf(w, format, a...) |
|
42 |
+ |
|
43 |
+ term.DisableEcho(inFd, oldState) |
|
44 |
+ |
|
45 |
+ input := readInput(r) |
|
46 |
+ |
|
47 |
+ defer term.RestoreTerminal(inFd, oldState) |
|
48 |
+ |
|
49 |
+ fmt.Fprintf(w, "\n") |
|
50 |
+ |
|
51 |
+ return input |
|
52 |
+ } |
|
53 |
+ glog.V(3).Infof("Stdin is not a terminal") |
|
54 |
+ return PromptForString(r, w, format, a...) |
|
55 |
+ } |
|
56 |
+ return PromptForString(r, w, format, a...) |
|
57 |
+} |
|
58 |
+ |
|
59 |
+// PromptForBool prompts for user input of a boolean value. The accepted values are: |
|
60 |
+// yes, y, true, t, 1 (not case sensitive) |
|
61 |
+// no, n, false, f, 0 (not case sensitive) |
|
62 |
+// A valid answer is mandatory so it will keep asking until an answer is provided. |
|
63 |
+func PromptForBool(r io.Reader, w io.Writer, format string, a ...interface{}) bool { |
|
64 |
+ if w == nil { |
|
65 |
+ w = os.Stdout |
|
66 |
+ } |
|
67 |
+ |
|
68 |
+ str := PromptForString(r, w, format, a...) |
|
69 |
+ switch strings.ToLower(str) { |
|
70 |
+ case "1", "t", "true", "y", "yes": |
|
71 |
+ return true |
|
72 |
+ case "0", "f", "false", "n", "no": |
|
73 |
+ return false |
|
74 |
+ } |
|
75 |
+ fmt.Println("Please enter 'yes' or 'no'.") |
|
76 |
+ return PromptForBool(r, w, format, a...) |
|
77 |
+} |
|
78 |
+ |
|
79 |
+// PromptForStringWithDefault prompts for user input but take a default in case nothing is provided. |
|
80 |
+func PromptForStringWithDefault(r io.Reader, w io.Writer, def string, format string, a ...interface{}) string { |
|
81 |
+ if w == nil { |
|
82 |
+ w = os.Stdout |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ s := PromptForString(r, w, format, a...) |
|
86 |
+ if len(s) == 0 { |
|
87 |
+ return def |
|
88 |
+ } |
|
89 |
+ return s |
|
90 |
+} |
|
91 |
+ |
|
92 |
+func readInput(r io.Reader) string { |
|
93 |
+ if kterm.IsTerminal(r) { |
|
94 |
+ return readInputFromTerminal(r) |
|
95 |
+ } |
|
96 |
+ return readInputFromReader(r) |
|
97 |
+} |
|
98 |
+ |
|
99 |
+func readInputFromTerminal(r io.Reader) string { |
|
100 |
+ reader := bufio.NewReader(r) |
|
101 |
+ result, _ := reader.ReadString('\n') |
|
102 |
+ return strings.TrimRight(result, "\r\n") |
|
103 |
+} |
|
104 |
+ |
|
105 |
+func readInputFromReader(r io.Reader) string { |
|
106 |
+ var result string |
|
107 |
+ fmt.Fscan(r, &result) |
|
108 |
+ return result |
|
109 |
+} |
|
110 |
+ |
|
111 |
+// IsTerminalReader returns whether the passed io.Reader is a terminal or not |
|
112 |
+func IsTerminalReader(r io.Reader) bool { |
|
113 |
+ file, ok := r.(*os.File) |
|
114 |
+ return ok && term.IsTerminal(file.Fd()) |
|
115 |
+} |
|
116 |
+ |
|
117 |
+// IsTerminalWriter returns whether the passed io.Writer is a terminal or not |
|
118 |
+func IsTerminalWriter(w io.Writer) bool { |
|
119 |
+ file, ok := w.(*os.File) |
|
120 |
+ return ok && term.IsTerminal(file.Fd()) |
|
121 |
+} |
0 | 122 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,33 @@ |
0 |
+package term |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "strings" |
|
4 |
+ "testing" |
|
5 |
+) |
|
6 |
+ |
|
7 |
+func TestReadInputFromTerminal(t *testing.T) { |
|
8 |
+ testcases := map[string]struct { |
|
9 |
+ Input string |
|
10 |
+ Output string |
|
11 |
+ }{ |
|
12 |
+ "empty": {}, |
|
13 |
+ "empty newline": {Input: "\n"}, |
|
14 |
+ "empty windows newline": {Input: "\r\n"}, |
|
15 |
+ "empty newline with extra": {Input: "\nextra"}, |
|
16 |
+ "leading space": {Input: " data", Output: " data"}, |
|
17 |
+ "leading space newline": {Input: " data\n", Output: " data"}, |
|
18 |
+ "leading space windows newline": {Input: " data\r\n", Output: " data"}, |
|
19 |
+ "leading space newline with extra": {Input: " data\nextra", Output: " data"}, |
|
20 |
+ "trailing space": {Input: " data ", Output: " data "}, |
|
21 |
+ "trailing space newline": {Input: " data \n", Output: " data "}, |
|
22 |
+ "trailing space windows newline": {Input: " data \r\n", Output: " data "}, |
|
23 |
+ "trailing space newline with extra": {Input: " data \nextra", Output: " data "}, |
|
24 |
+ } |
|
25 |
+ |
|
26 |
+ for k, tc := range testcases { |
|
27 |
+ output := readInputFromTerminal(strings.NewReader(tc.Input)) |
|
28 |
+ if output != tc.Output { |
|
29 |
+ t.Errorf("%s: Expected '%s', got '%s'", k, tc.Output, output) |
|
30 |
+ } |
|
31 |
+ } |
|
32 |
+} |
0 | 33 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,109 @@ |
0 |
+package term |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "io" |
|
4 |
+ "os" |
|
5 |
+ |
|
6 |
+ "github.com/docker/docker/pkg/term" |
|
7 |
+ wordwrap "github.com/mitchellh/go-wordwrap" |
|
8 |
+ kterm "k8s.io/kubernetes/pkg/util/term" |
|
9 |
+) |
|
10 |
+ |
|
11 |
+type wordWrapWriter struct { |
|
12 |
+ limit uint |
|
13 |
+ writer io.Writer |
|
14 |
+} |
|
15 |
+ |
|
16 |
+// NewResponsiveWriter creates a Writer that detects the column width of the |
|
17 |
+// terminal we are in, and adjusts every line width to fit and use recommended |
|
18 |
+// terminal sizes for better readability. Does proper word wrapping automatically. |
|
19 |
+// if terminal width >= 120 columns use 120 columns |
|
20 |
+// if terminal width >= 100 columns use 100 columns |
|
21 |
+// if terminal width >= 80 columns use 80 columns |
|
22 |
+// In case we're not in a terminal or if it's smaller than 80 columns width, |
|
23 |
+// doesn't do any wrapping. |
|
24 |
+func NewResponsiveWriter(w io.Writer) io.Writer { |
|
25 |
+ file, ok := w.(*os.File) |
|
26 |
+ if !ok { |
|
27 |
+ return w |
|
28 |
+ } |
|
29 |
+ fd := file.Fd() |
|
30 |
+ if !term.IsTerminal(fd) { |
|
31 |
+ return w |
|
32 |
+ } |
|
33 |
+ |
|
34 |
+ terminalSize := kterm.GetSize(fd) |
|
35 |
+ if terminalSize == nil { |
|
36 |
+ return w |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ var limit uint |
|
40 |
+ switch { |
|
41 |
+ case terminalSize.Width >= 120: |
|
42 |
+ limit = 120 |
|
43 |
+ case terminalSize.Width >= 100: |
|
44 |
+ limit = 100 |
|
45 |
+ case terminalSize.Width >= 80: |
|
46 |
+ limit = 80 |
|
47 |
+ } |
|
48 |
+ |
|
49 |
+ return NewWordWrapWriter(w, limit) |
|
50 |
+} |
|
51 |
+ |
|
52 |
+// NewWordWrapWriter is a Writer that supports a limit of characters on every line |
|
53 |
+// and does auto word wrapping that respects that limit. |
|
54 |
+func NewWordWrapWriter(w io.Writer, limit uint) io.Writer { |
|
55 |
+ return &wordWrapWriter{ |
|
56 |
+ limit: limit, |
|
57 |
+ writer: w, |
|
58 |
+ } |
|
59 |
+} |
|
60 |
+ |
|
61 |
+func (w wordWrapWriter) Write(p []byte) (nn int, err error) { |
|
62 |
+ if w.limit == 0 { |
|
63 |
+ return w.writer.Write(p) |
|
64 |
+ } |
|
65 |
+ original := string(p) |
|
66 |
+ wrapped := wordwrap.WrapString(original, w.limit) |
|
67 |
+ return w.writer.Write([]byte(wrapped)) |
|
68 |
+} |
|
69 |
+ |
|
70 |
+// NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns. |
|
71 |
+func NewPunchCardWriter(w io.Writer) io.Writer { |
|
72 |
+ return NewWordWrapWriter(w, 80) |
|
73 |
+} |
|
74 |
+ |
|
75 |
+type maxWidthWriter struct { |
|
76 |
+ maxWidth uint |
|
77 |
+ currentWidth uint |
|
78 |
+ written uint |
|
79 |
+ writer io.Writer |
|
80 |
+} |
|
81 |
+ |
|
82 |
+// NewMaxWidthWriter is a Writer that supports a limit of characters on every |
|
83 |
+// line, but doesn't do any word wrapping automatically. |
|
84 |
+func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer { |
|
85 |
+ return &maxWidthWriter{ |
|
86 |
+ maxWidth: maxWidth, |
|
87 |
+ writer: w, |
|
88 |
+ } |
|
89 |
+} |
|
90 |
+ |
|
91 |
+func (m maxWidthWriter) Write(p []byte) (nn int, err error) { |
|
92 |
+ for _, b := range p { |
|
93 |
+ if m.currentWidth == m.maxWidth { |
|
94 |
+ m.writer.Write([]byte{'\n'}) |
|
95 |
+ m.currentWidth = 0 |
|
96 |
+ } |
|
97 |
+ if b == '\n' { |
|
98 |
+ m.currentWidth = 0 |
|
99 |
+ } |
|
100 |
+ _, err := m.writer.Write([]byte{b}) |
|
101 |
+ if err != nil { |
|
102 |
+ return int(m.written), err |
|
103 |
+ } |
|
104 |
+ m.written++ |
|
105 |
+ m.currentWidth++ |
|
106 |
+ } |
|
107 |
+ return len(p), nil |
|
108 |
+} |
0 | 109 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,82 @@ |
0 |
+package term |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bytes" |
|
4 |
+ "strings" |
|
5 |
+ "testing" |
|
6 |
+) |
|
7 |
+ |
|
8 |
+const test = "Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin Origin" |
|
9 |
+ |
|
10 |
+func TestWordWrapWriter(t *testing.T) { |
|
11 |
+ testcases := map[string]struct { |
|
12 |
+ input string |
|
13 |
+ maxWidth uint |
|
14 |
+ }{ |
|
15 |
+ "max 10": {input: test, maxWidth: 10}, |
|
16 |
+ "max 80": {input: test, maxWidth: 80}, |
|
17 |
+ "max 120": {input: test, maxWidth: 120}, |
|
18 |
+ "max 5000": {input: test, maxWidth: 5000}, |
|
19 |
+ } |
|
20 |
+ for k, tc := range testcases { |
|
21 |
+ b := bytes.NewBufferString("") |
|
22 |
+ w := NewWordWrapWriter(b, tc.maxWidth) |
|
23 |
+ _, err := w.Write([]byte(tc.input)) |
|
24 |
+ if err != nil { |
|
25 |
+ t.Errorf("%s: Unexpected error: %v", k, err) |
|
26 |
+ } |
|
27 |
+ result := b.String() |
|
28 |
+ if !strings.Contains(result, "Origin") { |
|
29 |
+ t.Errorf("%s: Expected to contain \"Origin\"", k) |
|
30 |
+ } |
|
31 |
+ if len(result) < len(tc.input) { |
|
32 |
+ t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result) |
|
33 |
+ } |
|
34 |
+ for _, line := range strings.Split(result, "\n") { |
|
35 |
+ if len(line) > int(tc.maxWidth) { |
|
36 |
+ t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) |
|
37 |
+ } |
|
38 |
+ } |
|
39 |
+ for _, word := range strings.Split(result, " ") { |
|
40 |
+ if !strings.Contains(word, "Origin") { |
|
41 |
+ t.Errorf("%s: Unexpected broken word: %q", k, word) |
|
42 |
+ } |
|
43 |
+ } |
|
44 |
+ } |
|
45 |
+} |
|
46 |
+ |
|
47 |
+func TestMaxWidthWriter(t *testing.T) { |
|
48 |
+ testcases := map[string]struct { |
|
49 |
+ input string |
|
50 |
+ maxWidth uint |
|
51 |
+ }{ |
|
52 |
+ "max 10": {input: test, maxWidth: 10}, |
|
53 |
+ "max 80": {input: test, maxWidth: 80}, |
|
54 |
+ "max 120": {input: test, maxWidth: 120}, |
|
55 |
+ "max 5000": {input: test, maxWidth: 5000}, |
|
56 |
+ } |
|
57 |
+ for k, tc := range testcases { |
|
58 |
+ b := bytes.NewBufferString("") |
|
59 |
+ w := NewMaxWidthWriter(b, tc.maxWidth) |
|
60 |
+ _, err := w.Write([]byte(tc.input)) |
|
61 |
+ if err != nil { |
|
62 |
+ t.Errorf("%s: Unexpected error: %v", k, err) |
|
63 |
+ } |
|
64 |
+ result := b.String() |
|
65 |
+ if !strings.Contains(result, "Origin") { |
|
66 |
+ t.Errorf("%s: Expected to contain \"Origin\"", k) |
|
67 |
+ } |
|
68 |
+ if len(result) < len(tc.input) { |
|
69 |
+ t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result) |
|
70 |
+ } |
|
71 |
+ lines := strings.Split(result, "\n") |
|
72 |
+ for i, line := range lines { |
|
73 |
+ if len(line) > int(tc.maxWidth) { |
|
74 |
+ t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) |
|
75 |
+ } |
|
76 |
+ if i < len(lines)-1 && len(line) != int(tc.maxWidth) { |
|
77 |
+ t.Errorf("%s: Lines except the last one are expected to be exactly %d chars long, got %d: %q", k, tc.maxWidth, len(line), line) |
|
78 |
+ } |
|
79 |
+ } |
|
80 |
+ } |
|
81 |
+} |
0 | 82 |
deleted file mode 100644 |
... | ... |
@@ -1,122 +0,0 @@ |
1 |
-package util |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "bufio" |
|
5 |
- "fmt" |
|
6 |
- "io" |
|
7 |
- "os" |
|
8 |
- "strings" |
|
9 |
- |
|
10 |
- "github.com/docker/docker/pkg/term" |
|
11 |
- "github.com/golang/glog" |
|
12 |
- |
|
13 |
- kterm "k8s.io/kubernetes/pkg/util/term" |
|
14 |
-) |
|
15 |
- |
|
16 |
-// PromptForString takes an io.Reader and prompts for user input if it's a terminal, returning the result. |
|
17 |
-func PromptForString(r io.Reader, w io.Writer, format string, a ...interface{}) string { |
|
18 |
- if w == nil { |
|
19 |
- w = os.Stdout |
|
20 |
- } |
|
21 |
- |
|
22 |
- fmt.Fprintf(w, format, a...) |
|
23 |
- return readInput(r) |
|
24 |
-} |
|
25 |
- |
|
26 |
-// PromptForPasswordString prompts for user input by disabling echo in terminal, useful for password prompt. |
|
27 |
-func PromptForPasswordString(r io.Reader, w io.Writer, format string, a ...interface{}) string { |
|
28 |
- if w == nil { |
|
29 |
- w = os.Stdout |
|
30 |
- } |
|
31 |
- |
|
32 |
- if file, ok := r.(*os.File); ok { |
|
33 |
- inFd := file.Fd() |
|
34 |
- |
|
35 |
- if term.IsTerminal(inFd) { |
|
36 |
- oldState, err := term.SaveState(inFd) |
|
37 |
- if err != nil { |
|
38 |
- glog.V(3).Infof("Unable to save terminal state") |
|
39 |
- return PromptForString(r, w, format, a...) |
|
40 |
- } |
|
41 |
- |
|
42 |
- fmt.Fprintf(w, format, a...) |
|
43 |
- |
|
44 |
- term.DisableEcho(inFd, oldState) |
|
45 |
- |
|
46 |
- input := readInput(r) |
|
47 |
- |
|
48 |
- defer term.RestoreTerminal(inFd, oldState) |
|
49 |
- |
|
50 |
- fmt.Fprintf(w, "\n") |
|
51 |
- |
|
52 |
- return input |
|
53 |
- } |
|
54 |
- glog.V(3).Infof("Stdin is not a terminal") |
|
55 |
- return PromptForString(r, w, format, a...) |
|
56 |
- } |
|
57 |
- return PromptForString(r, w, format, a...) |
|
58 |
-} |
|
59 |
- |
|
60 |
-// PromptForBool prompts for user input of a boolean value. The accepted values are: |
|
61 |
-// yes, y, true, t, 1 (not case sensitive) |
|
62 |
-// no, n, false, f, 0 (not case sensitive) |
|
63 |
-// A valid answer is mandatory so it will keep asking until an answer is provided. |
|
64 |
-func PromptForBool(r io.Reader, w io.Writer, format string, a ...interface{}) bool { |
|
65 |
- if w == nil { |
|
66 |
- w = os.Stdout |
|
67 |
- } |
|
68 |
- |
|
69 |
- str := PromptForString(r, w, format, a...) |
|
70 |
- switch strings.ToLower(str) { |
|
71 |
- case "1", "t", "true", "y", "yes": |
|
72 |
- return true |
|
73 |
- case "0", "f", "false", "n", "no": |
|
74 |
- return false |
|
75 |
- } |
|
76 |
- fmt.Println("Please enter 'yes' or 'no'.") |
|
77 |
- return PromptForBool(r, w, format, a...) |
|
78 |
-} |
|
79 |
- |
|
80 |
-// PromptForStringWithDefault prompts for user input but take a default in case nothing is provided. |
|
81 |
-func PromptForStringWithDefault(r io.Reader, w io.Writer, def string, format string, a ...interface{}) string { |
|
82 |
- if w == nil { |
|
83 |
- w = os.Stdout |
|
84 |
- } |
|
85 |
- |
|
86 |
- s := PromptForString(r, w, format, a...) |
|
87 |
- if len(s) == 0 { |
|
88 |
- return def |
|
89 |
- } |
|
90 |
- return s |
|
91 |
-} |
|
92 |
- |
|
93 |
-func readInput(r io.Reader) string { |
|
94 |
- if kterm.IsTerminal(r) { |
|
95 |
- return readInputFromTerminal(r) |
|
96 |
- } |
|
97 |
- return readInputFromReader(r) |
|
98 |
-} |
|
99 |
- |
|
100 |
-func readInputFromTerminal(r io.Reader) string { |
|
101 |
- reader := bufio.NewReader(r) |
|
102 |
- result, _ := reader.ReadString('\n') |
|
103 |
- return strings.TrimRight(result, "\r\n") |
|
104 |
-} |
|
105 |
- |
|
106 |
-func readInputFromReader(r io.Reader) string { |
|
107 |
- var result string |
|
108 |
- fmt.Fscan(r, &result) |
|
109 |
- return result |
|
110 |
-} |
|
111 |
- |
|
112 |
-// IsTerminalReader returns whether the passed io.Reader is a terminal or not |
|
113 |
-func IsTerminalReader(r io.Reader) bool { |
|
114 |
- file, ok := r.(*os.File) |
|
115 |
- return ok && term.IsTerminal(file.Fd()) |
|
116 |
-} |
|
117 |
- |
|
118 |
-// IsTerminalWriter returns whether the passed io.Writer is a terminal or not |
|
119 |
-func IsTerminalWriter(w io.Writer) bool { |
|
120 |
- file, ok := w.(*os.File) |
|
121 |
- return ok && term.IsTerminal(file.Fd()) |
|
122 |
-} |
123 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,33 +0,0 @@ |
1 |
-package util |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "strings" |
|
5 |
- "testing" |
|
6 |
-) |
|
7 |
- |
|
8 |
-func TestReadInputFromTerminal(t *testing.T) { |
|
9 |
- testcases := map[string]struct { |
|
10 |
- Input string |
|
11 |
- Output string |
|
12 |
- }{ |
|
13 |
- "empty": {}, |
|
14 |
- "empty newline": {Input: "\n"}, |
|
15 |
- "empty windows newline": {Input: "\r\n"}, |
|
16 |
- "empty newline with extra": {Input: "\nextra"}, |
|
17 |
- "leading space": {Input: " data", Output: " data"}, |
|
18 |
- "leading space newline": {Input: " data\n", Output: " data"}, |
|
19 |
- "leading space windows newline": {Input: " data\r\n", Output: " data"}, |
|
20 |
- "leading space newline with extra": {Input: " data\nextra", Output: " data"}, |
|
21 |
- "trailing space": {Input: " data ", Output: " data "}, |
|
22 |
- "trailing space newline": {Input: " data \n", Output: " data "}, |
|
23 |
- "trailing space windows newline": {Input: " data \r\n", Output: " data "}, |
|
24 |
- "trailing space newline with extra": {Input: " data \nextra", Output: " data "}, |
|
25 |
- } |
|
26 |
- |
|
27 |
- for k, tc := range testcases { |
|
28 |
- output := readInputFromTerminal(strings.NewReader(tc.Input)) |
|
29 |
- if output != tc.Output { |
|
30 |
- t.Errorf("%s: Expected '%s', got '%s'", k, tc.Output, output) |
|
31 |
- } |
|
32 |
- } |
|
33 |
-} |
... | ... |
@@ -11,7 +11,7 @@ import ( |
11 | 11 |
|
12 | 12 |
"github.com/golang/glog" |
13 | 13 |
|
14 |
- "github.com/openshift/origin/pkg/cmd/util" |
|
14 |
+ "github.com/openshift/origin/pkg/cmd/util/term" |
|
15 | 15 |
) |
16 | 16 |
|
17 | 17 |
func BasicEnabled() bool { |
... | ... |
@@ -70,12 +70,12 @@ func (c *BasicChallengeHandler) HandleChallenge(requestURL string, headers http. |
70 | 70 |
fmt.Fprintf(w, "Authentication required for %s\n", c.Host) |
71 | 71 |
} |
72 | 72 |
if missingUsername { |
73 |
- username = util.PromptForString(c.Reader, w, "Username: ") |
|
73 |
+ username = term.PromptForString(c.Reader, w, "Username: ") |
|
74 | 74 |
} else { |
75 | 75 |
fmt.Fprintf(w, "Username: %s\n", username) |
76 | 76 |
} |
77 | 77 |
if missingPassword { |
78 |
- password = util.PromptForPasswordString(c.Reader, w, "Password: ") |
|
78 |
+ password = term.PromptForPasswordString(c.Reader, w, "Password: ") |
|
79 | 79 |
} |
80 | 80 |
// remember so we don't re-prompt |
81 | 81 |
c.prompted = true |