cmd/tailscale/cli: add ability to set short names for profiles

This adds a `--nickname` flag to `tailscale login|set`.

Updates #713

Signed-off-by: Maisem Ali <maisem@tailscale.com>
mihaip/delete-all-profiles
Maisem Ali 2 years ago committed by Maisem Ali
parent 575fd5f22b
commit b94b91c168

@ -480,6 +480,19 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
distro: "", // not Synology
want: accidentalUpPrefix + " --hostname=foo --accept-routes",
},
{
name: "profile_name_ignored_in_up",
flags: []string{"--hostname=foo"},
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
CorpDNS: true,
AllowSingleHosts: true,
NetfilterMode: preftype.NetfilterOn,
ProfileName: "foo",
},
goos: "linux",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

@ -27,6 +27,6 @@ This command is currently in alpha and may change in the future.`,
if err := localClient.SwitchToEmptyProfile(ctx); err != nil {
return err
}
return runUp(ctx, args, loginArgs)
return runUp(ctx, "login", args, loginArgs)
},
}

@ -43,11 +43,13 @@ type setArgsT struct {
advertiseDefaultRoute bool
opUser string
acceptedRisks string
profileName string
}
func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet {
setf := newFlagSet("set")
setf.StringVar(&setArgs.profileName, "nickname", "", "nickname for the login profile")
setf.BoolVar(&setArgs.acceptRoutes, "accept-routes", false, "accept routes advertised by other Tailscale nodes")
setf.BoolVar(&setArgs.acceptDNS, "accept-dns", false, "accept DNS configuration from the admin panel")
setf.StringVar(&setArgs.exitNodeIP, "exit-node", "", "Tailscale exit node (IP or base name) for internet traffic, or empty string to not use an exit node")
@ -81,6 +83,7 @@ func runSet(ctx context.Context, args []string) (retErr error) {
maskedPrefs := &ipn.MaskedPrefs{
Prefs: ipn.Prefs{
ProfileName: setArgs.profileName,
RouteAll: setArgs.acceptRoutes,
CorpDNS: setArgs.acceptDNS,
ExitNodeAllowLANAccess: setArgs.exitNodeAllowLANAccess,
@ -132,6 +135,11 @@ func runSet(ctx context.Context, args []string) (retErr error) {
return err
}
}
checkPrefs := curPrefs.Clone()
checkPrefs.ApplyEdits(maskedPrefs)
if err := localClient.CheckPrefs(ctx, checkPrefs); err != nil {
return err
}
_, err = localClient.EditPrefs(ctx, maskedPrefs)
return err

@ -60,7 +60,7 @@ settings.)
`),
FlagSet: upFlagSet,
Exec: func(ctx context.Context, args []string) error {
return runUp(ctx, args, upArgsGlobal)
return runUp(ctx, "up", args, upArgsGlobal)
},
}
@ -122,6 +122,10 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
}
upf.DurationVar(&upArgs.timeout, "timeout", 0, "maximum amount of time to wait for tailscaled to enter a Running state; default (0s) blocks forever")
if cmd == "login" {
upf.StringVar(&upArgs.profileName, "nickname", "", "short name for the login profile")
}
if cmd == "up" {
// Some flags are only for "up", not "login".
upf.BoolVar(&upArgs.json, "json", false, "output in JSON format (WARNING: format subject to change)")
@ -129,6 +133,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
upf.BoolVar(&upArgs.forceReauth, "force-reauth", false, "force reauthentication")
registerAcceptRiskFlag(upf, &upArgs.acceptedRisks)
}
return upf
}
@ -163,6 +168,7 @@ type upArgsT struct {
json bool
timeout time.Duration
acceptedRisks string
profileName string
}
func (a upArgsT) getAuthKey() (string, error) {
@ -343,6 +349,7 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
prefs.Hostname = upArgs.hostname
prefs.ForceDaemon = upArgs.forceDaemon
prefs.OperatorUser = upArgs.opUser
prefs.ProfileName = upArgs.profileName
if goos == "linux" {
prefs.NoSNAT = !upArgs.snat
@ -437,7 +444,7 @@ func presentSSHToggleRisk(wantSSH, haveSSH bool, acceptedRisks string) error {
return presentRiskToUser(riskLoseSSH, `You are connected using Tailscale SSH; this action will result in your session disconnecting.`, acceptedRisks)
}
func runUp(ctx context.Context, args []string, upArgs upArgsT) (retErr error) {
func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retErr error) {
var egg bool
if len(args) > 0 {
egg = fmt.Sprint(args) == "[up down down left right left right b a]"
@ -496,6 +503,11 @@ func runUp(ctx context.Context, args []string, upArgs upArgsT) (retErr error) {
if err != nil {
return err
}
if cmd == "up" {
// "tailscale up" should not be able to change the
// profile name.
prefs.ProfileName = curPrefs.ProfileName
}
env := upCheckEnv{
goos: effectiveGOOS(),
@ -764,6 +776,7 @@ func init() {
addPrefFlagMapping("unattended", "ForceDaemon")
addPrefFlagMapping("operator", "OperatorUser")
addPrefFlagMapping("ssh", "RunSSH")
addPrefFlagMapping("nickname", "ProfileName")
}
func addPrefFlagMapping(flagName string, prefNames ...string) {

@ -2109,6 +2109,9 @@ func (b *LocalBackend) checkPrefsLocked(p *ipn.Prefs) error {
// Keep this one just for testing.
errs = append(errs, errors.New("bad hostname [test]"))
}
if err := b.checkProfileNameLocked(p); err != nil {
errs = append(errs, err)
}
if err := b.checkSSHPrefsLocked(p); err != nil {
errs = append(errs, err)
}
@ -2226,6 +2229,23 @@ func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
return stripKeysFromPrefs(newPrefs), nil
}
func (b *LocalBackend) checkProfileNameLocked(p *ipn.Prefs) error {
if p.ProfileName == "" {
// It is always okay to clear the profile name.
return nil
}
id := b.pm.ProfileIDForName(p.ProfileName)
if id == "" {
// No profile with that name exists. That's fine.
return nil
}
if id != b.pm.CurrentProfile().ID {
// Name is already in use by another profile.
return fmt.Errorf("profile name %q already in use", p.ProfileName)
}
return nil
}
// SetPrefs saves new user preferences and propagates them throughout
// the system. Implements Backend.
func (b *LocalBackend) SetPrefs(newp *ipn.Prefs) {

@ -96,6 +96,16 @@ func (pm *profileManager) findProfilesByUserID(userID tailcfg.UserID) []*ipn.Log
return out
}
// ProfileIDForName returns the profile ID for the profile with the
// given name. It returns "" if no such profile exists.
func (pm *profileManager) ProfileIDForName(name string) ipn.ProfileID {
p := pm.findProfileByName(name)
if p == nil {
return ""
}
return p.ID
}
func (pm *profileManager) findProfileByName(name string) *ipn.LoginProfile {
for _, p := range pm.knownProfiles {
if p.Name == name {

@ -190,7 +190,7 @@ type Prefs struct {
// operate tailscaled without being root or using sudo.
OperatorUser string `json:",omitempty"`
// ProfileName is the desired name of the profile. If empty, then the users
// ProfileName is the desired name of the profile. If empty, then the user's
// LoginName is used. It is only used for display purposes in the client UI
// and CLI.
ProfileName string `json:",omitempty"`

Loading…
Cancel
Save