-
-
Notifications
You must be signed in to change notification settings - Fork 513
/
cmd.go
206 lines (183 loc) · 6.76 KB
/
cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package cli
import (
"context"
"runtime"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/datawire/ambassador/v2/pkg/kates"
"github.com/telepresenceio/telepresence/v2/pkg/client/errcat"
)
var help = `Telepresence can connect to a cluster and route all outbound traffic from your
workstation to that cluster so that software running locally can communicate
as if it executed remotely, inside the cluster. This is achieved using the
command:
telepresence connect
Telepresence can also intercept traffic intended for a specific service in a
cluster and redirect it to your local workstation:
telepresence intercept <name of service>
Telepresence uses background processes to manage the cluster session. One of
the processes runs with superuser privileges because it modifies the network.
Unless the daemons are already started, an attempt will be made to start them.
This will involve a call to sudo unless this command is run as root (not
recommended) which in turn may result in a password prompt.`
// global options
var dnsIP string
var mappedNamespaces []string
var kubeFlags *pflag.FlagSet
var kubeConfig *kates.ConfigFlags
// OnlySubcommands is a cobra.PositionalArgs that is similar to cobra.NoArgs, but prints a better
// error message.
func OnlySubcommands(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return nil
}
err := errcat.User.Newf("invalid subcommand %q", args[0])
if cmd.SuggestionsMinimumDistance <= 0 {
cmd.SuggestionsMinimumDistance = 2
}
if suggestions := cmd.SuggestionsFor(args[0]); len(suggestions) > 0 {
err = errcat.User.Newf("%w\nDid you mean one of these?\n\t%s", err, strings.Join(suggestions, "\n\t"))
}
return cmd.FlagErrorFunc()(cmd, err)
}
// PerhapsLegacyCommands is like OnlySubcommands but performs some initial check for legacy flags
func PerhapsLegacyCommands(cmd *cobra.Command, args []string) error {
// If a user is using a flag that is coming from telepresence 1, we try to
// construct the tp2 command based on their input. If the args passed to
// telepresence are one of the flags we recognize, we don't want to error
// out here.
tp1Flags := []string{"--swap-deployment", "-s", "--run", "--run-shell", "--docker-run", "--help"}
for _, v := range args {
for _, flag := range tp1Flags {
if v == flag {
return nil
}
}
}
return OnlySubcommands(cmd, args)
}
// RunSubcommands is for use as a cobra.Command.RunE for commands that don't do anything themselves
// but have subcommands. In such cases, it is important to set RunE even though there's nothing to
// run, because otherwise cobra will treat that as "success", and it shouldn't be "success" if the
// user typos a command and types something invalid.
func RunSubcommands(cmd *cobra.Command, args []string) error {
cmd.SetOut(cmd.ErrOrStderr())
// determine if --help was explicitly asked for
var usedHelpFlag bool
for _, arg := range args {
if arg == "--help" {
usedHelpFlag = true
}
}
// If there are no args or --help was used, then it's not a legacy
// Telepresence command so we return the help text
if len(args) == 0 || usedHelpFlag {
cmd.HelpFunc()(cmd, args)
return nil
}
if err := checkLegacyCmd(cmd, args); err != nil {
return err
}
return nil
}
// Command returns the top level "telepresence" CLI command
func Command(ctx context.Context) *cobra.Command {
rootCmd := &cobra.Command{
Use: "telepresence",
Args: PerhapsLegacyCommands,
Short: "Connect your workstation to a Kubernetes cluster",
Long: help,
RunE: RunSubcommands,
SilenceErrors: true, // main() will handle it after .ExecuteContext() returns
SilenceUsage: true, // our FlagErrorFunc will handle it
DisableFlagParsing: true, // Bc of the legacyCommand parsing, see legacy_command.go
}
// Since we had to DisableFlagParsing so we can parse legacy commands, this
// doesn't do anything. Leaving this commented because I don't know if we'll
// leave legacy command parsing forever, in which case we'd want to uncomment
// this
/*
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
if err == nil {
return nil
}
// If the error is multiple lines, include an extra blank line before the "See
// --help" line.
errStr := strings.TrimRight(err.Error(), "\n")
if strings.Contains(errStr, "\n") {
errStr += "\n"
}
fmt.Fprintf(cmd.ErrOrStderr(), "%s: %s\nSee '%s --help'.\n", cmd.CommandPath(), errStr, cmd.CommandPath())
os.Exit(2)
return nil
})
*/
rootCmd.InitDefaultHelpCmd()
AddCommandGroups(rootCmd, []CommandGroup{
{
Name: "Session Commands",
Commands: []*cobra.Command{connectCommand(), LoginCommand(), LogoutCommand(), LicenseCommand(), statusCommand(), quitCommand()},
},
{
Name: "Traffic Commands",
Commands: []*cobra.Command{listCommand(), interceptCommand(ctx), leaveCommand(), previewCommand()},
},
{
Name: "Debug Commands",
Commands: []*cobra.Command{loglevelCommand(), gatherLogsCommand()},
},
{
Name: "Other Commands",
Commands: []*cobra.Command{versionCommand(), uninstallCommand(), dashboardCommand(), ClusterIdCommand(), genYAMLCommand(), vpnDiagCommand()},
},
})
initGlobalFlagGroups()
for _, group := range globalFlagGroups {
rootCmd.PersistentFlags().AddFlagSet(group.Flags)
}
return rootCmd
}
func initGlobalFlagGroups() {
globalFlagGroups = []FlagGroup{
{
Name: "Kubernetes flags",
Flags: func() *pflag.FlagSet {
kubeFlags = pflag.NewFlagSet("", 0)
kubeConfig = kates.NewConfigFlags(false)
kubeConfig.Namespace = nil // some of the subcommands, like "connect", don't take --namespace
kubeConfig.AddFlags(kubeFlags)
return kubeFlags
}(),
}}
globalFlagGroups = append(globalFlagGroups, FlagGroup{
Name: "Telepresence networking flags",
Flags: func() *pflag.FlagSet {
netflags := pflag.NewFlagSet("", 0)
// TODO: Those flags aren't applicable on a Linux with systemd-resolved configured either but
// that's unknown until it's been tested during the first connect attempt.
if runtime.GOOS != "darwin" && runtime.GOOS != "windows" {
netflags.StringVarP(&dnsIP,
"dns", "", "",
"DNS IP address to intercept locally. Defaults to the first nameserver listed in /etc/resolv.conf.",
)
}
netflags.StringSliceVar(&mappedNamespaces,
"mapped-namespaces", nil, ``+
`Comma separated list of namespaces considered by DNS resolver and NAT for outbound connections. `+
`Defaults to all namespaces`)
return netflags
}(),
})
globalFlagGroups = append(globalFlagGroups, FlagGroup{
Name: "other Telepresence flags",
Flags: func() *pflag.FlagSet {
flags := pflag.NewFlagSet("", 0)
flags.Bool(
"no-report", false,
"turn off anonymous crash reports and log submission on failure",
)
return flags
}(),
})
}