Skip to content

Commit

Permalink
cmd/tailscale,ipn: add auto-update flags and prefs (#8861)
Browse files Browse the repository at this point in the history
The flags are hidden for now. Adding propagation to tailscaled and
persistence only. The prefs field is wrapped in a struct to allow for
future expansion (like update schedule).

Updates #6907

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
  • Loading branch information
awly committed Sep 1, 2023
1 parent 055fdb2 commit 34e3450
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 16 deletions.
24 changes: 24 additions & 0 deletions cmd/tailscale/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
NetfilterMode: preftype.NetfilterOn,
CorpDNS: true,
AllowSingleHosts: true,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand All @@ -569,6 +573,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
AllowSingleHosts: true,
RouteAll: true,
NetfilterMode: preftype.NetfilterOn,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand All @@ -584,6 +592,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
netip.MustParsePrefix("::/0"),
},
NetfilterMode: preftype.NetfilterOn,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand Down Expand Up @@ -670,6 +682,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
WantRunning: true,
NetfilterMode: preftype.NetfilterNoDivert,
NoSNAT: true,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand All @@ -683,6 +699,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
WantRunning: true,
NetfilterMode: preftype.NetfilterOff,
NoSNAT: true,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand All @@ -698,6 +718,10 @@ func TestPrefsFromUpArgs(t *testing.T) {
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
},
AutoUpdate: ipn.AutoUpdatePrefs{
Check: true,
Apply: false,
},
},
},
{
Expand Down
15 changes: 15 additions & 0 deletions cmd/tailscale/cli/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/netip"

"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/clientupdate"
"tailscale.com/ipn"
"tailscale.com/net/netutil"
"tailscale.com/net/tsaddr"
Expand Down Expand Up @@ -46,6 +47,8 @@ type setArgsT struct {
acceptedRisks string
profileName string
forceDaemon bool
updateCheck bool
updateApply bool
}

func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet {
Expand All @@ -61,6 +64,8 @@ func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet {
setf.StringVar(&setArgs.hostname, "hostname", "", "hostname to use instead of the one provided by the OS")
setf.StringVar(&setArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. \"10.0.0.0/8,192.168.0.0/24\") or empty string to not advertise routes")
setf.BoolVar(&setArgs.advertiseDefaultRoute, "advertise-exit-node", false, "offer to be an exit node for internet traffic for the tailnet")
setf.BoolVar(&setArgs.updateCheck, "update-check", true, "HIDDEN: notify about available Tailscale updates")
setf.BoolVar(&setArgs.updateApply, "auto-update", false, "HIDDEN: automatically update to the latest available version")
if safesocket.GOOSUsesPeerCreds(goos) {
setf.StringVar(&setArgs.opUser, "operator", "", "Unix username to allow to operate on tailscaled without sudo")
}
Expand Down Expand Up @@ -99,6 +104,10 @@ func runSet(ctx context.Context, args []string) (retErr error) {
Hostname: setArgs.hostname,
OperatorUser: setArgs.opUser,
ForceDaemon: setArgs.forceDaemon,
AutoUpdate: ipn.AutoUpdatePrefs{
Check: setArgs.updateCheck,
Apply: setArgs.updateApply,
},
},
}

Expand Down Expand Up @@ -143,6 +152,12 @@ func runSet(ctx context.Context, args []string) (retErr error) {
return err
}
}
if maskedPrefs.AutoUpdateSet {
_, err := clientupdate.NewUpdater(clientupdate.Arguments{})
if errors.Is(err, errors.ErrUnsupported) {
return errors.New("automatic updates are not supported on this platform")
}
}
checkPrefs := curPrefs.Clone()
checkPrefs.ApplyEdits(maskedPrefs)
if err := localClient.CheckPrefs(ctx, checkPrefs); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions cmd/tailscale/cli/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ func newUpFlagSet(goos string, upArgs *upArgsT, cmd string) *flag.FlagSet {
}
upf := newFlagSet(cmd)

// When adding new flags, prefer to put them under "tailscale set" instead
// of here. Setting preferences via "tailscale up" is deprecated.
upf.BoolVar(&upArgs.qr, "qr", false, "show QR code for login URLs")
upf.StringVar(&upArgs.authKeyOrFile, "auth-key", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`)

Expand Down Expand Up @@ -712,6 +714,8 @@ func init() {
addPrefFlagMapping("operator", "OperatorUser")
addPrefFlagMapping("ssh", "RunSSH")
addPrefFlagMapping("nickname", "ProfileName")
addPrefFlagMapping("update-check", "AutoUpdate")
addPrefFlagMapping("auto-update", "AutoUpdate")
}

func addPrefFlagMapping(flagName string, prefNames ...string) {
Expand Down
1 change: 1 addition & 0 deletions ipn/ipn_clone.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ipn/ipn_view.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ipn/ipnlocal/c2n.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ func (b *LocalBackend) handleC2NUpdate(w http.ResponseWriter, r *http.Request) {
//
// Note that we create the Updater solely to check for errors; we do not
// invoke it here. For this purpose, it is ok to pass it a zero Arguments.
prefs := b.Prefs().AutoUpdate()
_, err := clientupdate.NewUpdater(clientupdate.Arguments{})
res := tailcfg.C2NUpdateResponse{
Enabled: envknob.AllowsRemoteUpdate(),
Enabled: envknob.AllowsRemoteUpdate() || prefs.Apply,
Supported: err == nil && !version.IsMacSysExt(),
}

Expand Down
41 changes: 40 additions & 1 deletion ipn/prefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ type Prefs struct {
// and CLI.
ProfileName string `json:",omitempty"`

// AutoUpdate sets the auto-update preferences for the node agent. See
// AutoUpdatePrefs docs for more details.
AutoUpdate AutoUpdatePrefs

// The Persist field is named 'Config' in the file for backward
// compatibility with earlier versions.
// TODO(apenwarr): We should move this out of here, it's not a pref.
Expand All @@ -204,6 +208,18 @@ type Prefs struct {
Persist *persist.Persist `json:"Config"`
}

// AutoUpdatePrefs are the auto update settings for the node agent.
type AutoUpdatePrefs struct {
// Check specifies whether background checks for updates are enabled. When
// enabled, tailscaled will periodically check for available updates and
// notify the user about them.
Check bool
// Apply specifies whether background auto-updates are enabled. When
// enabled, tailscaled will apply available updates in the background.
// Check must also be set when Apply is set.
Apply bool
}

// MaskedPrefs is a Prefs with an associated bitmask of which fields are set.
type MaskedPrefs struct {
Prefs
Expand All @@ -229,6 +245,7 @@ type MaskedPrefs struct {
NetfilterModeSet bool `json:",omitempty"`
OperatorUserSet bool `json:",omitempty"`
ProfileNameSet bool `json:",omitempty"`
AutoUpdateSet bool `json:",omitempty"`
}

// ApplyEdits mutates p, assigning fields from m.Prefs for each MaskedPrefs
Expand Down Expand Up @@ -284,6 +301,12 @@ func (m *MaskedPrefs) Pretty() string {
if v.Type().Elem().Kind() == reflect.String {
return "%s=%q"
}
case reflect.Struct:
return "%s=%+v"
case reflect.Pointer:
if v.Type().Elem().Kind() == reflect.Struct {
return "%s=%+v"
}
}
return "%s=%v"
}
Expand Down Expand Up @@ -360,6 +383,7 @@ func (p *Prefs) pretty(goos string) string {
if p.OperatorUser != "" {
fmt.Fprintf(&sb, "op=%q ", p.OperatorUser)
}
sb.WriteString(p.AutoUpdate.Pretty())
if p.Persist != nil {
sb.WriteString(p.Persist.Pretty())
} else {
Expand Down Expand Up @@ -414,7 +438,18 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
compareIPNets(p.AdvertiseRoutes, p2.AdvertiseRoutes) &&
compareStrings(p.AdvertiseTags, p2.AdvertiseTags) &&
p.Persist.Equals(p2.Persist) &&
p.ProfileName == p2.ProfileName
p.ProfileName == p2.ProfileName &&
p.AutoUpdate == p2.AutoUpdate
}

func (au AutoUpdatePrefs) Pretty() string {
if au.Apply {
return "update=on "
}
if au.Check {
return "update=check "
}
return "update=off "
}

func compareIPNets(a, b []netip.Prefix) bool {
Expand Down Expand Up @@ -459,6 +494,10 @@ func NewPrefs() *Prefs {
CorpDNS: true,
WantRunning: false,
NetfilterMode: preftype.NetfilterOn,
AutoUpdate: AutoUpdatePrefs{
Check: true,
Apply: false,
},
}
}

Expand Down
Loading

0 comments on commit 34e3450

Please sign in to comment.