@ -25,6 +25,7 @@ import (
"slices"
"slices"
"strconv"
"strconv"
"strings"
"strings"
"sync"
"sync/atomic"
"sync/atomic"
"testing"
"testing"
"time"
"time"
@ -1412,14 +1413,27 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
wantNode0PeerCount ( expectedPeers ) // all existing peers and the new node
wantNode0PeerCount ( expectedPeers ) // all existing peers and the new node
}
}
func TestAutoUpdateDefaults ( t * testing . T ) {
func TestAutoUpdateDefaults ( t * testing . T ) { testAutoUpdateDefaults ( t , false ) }
if ! feature . CanAutoUpdate ( ) {
func TestAutoUpdateDefaults_cap ( t * testing . T ) { testAutoUpdateDefaults ( t , true ) }
t . Skip ( "auto-updates not supported on this platform" )
}
// useCap is whether to use NodeAttrDefaultAutoUpdate (as opposed to the old
// DeprecatedDefaultAutoUpdate top-level MapResponse field).
func testAutoUpdateDefaults ( t * testing . T , useCap bool ) {
t . Cleanup ( feature . HookCanAutoUpdate . SetForTest ( func ( ) bool { return true } ) )
tstest . Shard ( t )
tstest . Shard ( t )
tstest . Parallel ( t )
env := NewTestEnv ( t )
env := NewTestEnv ( t )
var (
modifyMu sync . Mutex
modifyFirstMapResponse = func ( * tailcfg . MapResponse , * tailcfg . MapRequest ) { }
)
env . Control . ModifyFirstMapResponse = func ( mr * tailcfg . MapResponse , req * tailcfg . MapRequest ) {
modifyMu . Lock ( )
defer modifyMu . Unlock ( )
modifyFirstMapResponse ( mr , req )
}
checkDefault := func ( n * TestNode , want bool ) error {
checkDefault := func ( n * TestNode , want bool ) error {
enabled , ok := n . diskPrefs ( ) . AutoUpdate . Apply . Get ( )
enabled , ok := n . diskPrefs ( ) . AutoUpdate . Apply . Get ( )
if ! ok {
if ! ok {
@ -1431,17 +1445,23 @@ func TestAutoUpdateDefaults(t *testing.T) {
return nil
return nil
}
}
sendAndCheckDefault := func ( t * testing . T , n * TestNode , send , want bool ) {
setDefaultAutoUpdate := func ( send bool ) {
t . Helper ( )
modifyMu . Lock ( )
if ! env . Control . AddRawMapResponse ( n . MustStatus ( ) . Self . PublicKey , & tailcfg . MapResponse {
defer modifyMu . Unlock ( )
DefaultAutoUpdate : opt . NewBool ( send ) ,
modifyFirstMapResponse = func ( mr * tailcfg . MapResponse , req * tailcfg . MapRequest ) {
} ) {
if mr . Node == nil {
t . Fatal ( "failed to send MapResponse to node" )
mr . Node = & tailcfg . Node { }
}
if useCap {
if mr . Node . CapMap == nil {
mr . Node . CapMap = make ( tailcfg . NodeCapMap )
}
mr . Node . CapMap [ tailcfg . NodeAttrDefaultAutoUpdate ] = [ ] tailcfg . RawMessage {
tailcfg . RawMessage ( fmt . Sprintf ( "%t" , send ) ) ,
}
} else {
mr . DeprecatedDefaultAutoUpdate = opt . NewBool ( send )
}
}
if err := tstest . WaitFor ( 2 * time . Second , func ( ) error {
return checkDefault ( n , want )
} ) ; err != nil {
t . Fatal ( err )
}
}
}
}
@ -1452,29 +1472,54 @@ func TestAutoUpdateDefaults(t *testing.T) {
{
{
desc : "tailnet-default-false" ,
desc : "tailnet-default-false" ,
run : func ( t * testing . T , n * TestNode ) {
run : func ( t * testing . T , n * TestNode ) {
// First received default "false".
sendAndCheckDefault ( t , n , false , false )
// First the server sends "false", and client should remember that.
// Should not be changed even if sent "true" later.
setDefaultAutoUpdate ( false )
sendAndCheckDefault ( t , n , true , false )
n . MustUp ( )
n . AwaitRunning ( )
checkDefault ( n , false )
// Now we disconnect and change the server to send "true", which
// the client should ignore, having previously remembered
// "false".
n . MustDown ( )
setDefaultAutoUpdate ( true ) // control sends default "true"
n . MustUp ( )
n . AwaitRunning ( )
checkDefault ( n , false ) // still false
// But can be changed explicitly by the user.
// But can be changed explicitly by the user.
if out , err := n . TailscaleForOutput ( "set" , "--auto-update" ) . CombinedOutput ( ) ; err != nil {
if out , err := n . TailscaleForOutput ( "set" , "--auto-update" ) . CombinedOutput ( ) ; err != nil {
t . Fatalf ( "failed to enable auto-update on node: %v\noutput: %s" , err , out )
t . Fatalf ( "failed to enable auto-update on node: %v\noutput: %s" , err , out )
}
}
sendAndCheckDefault ( t , n , false , true )
checkDefault( n , true )
} ,
} ,
} ,
} ,
{
{
desc : "tailnet-default-true" ,
desc : "tailnet-default-true" ,
run : func ( t * testing . T , n * TestNode ) {
run : func ( t * testing . T , n * TestNode ) {
// First received default "true".
// Same as above but starting with default "true".
sendAndCheckDefault ( t , n , true , true )
// Should not be changed even if sent "false" later.
// First the server sends "true", and client should remember that.
sendAndCheckDefault ( t , n , false , true )
setDefaultAutoUpdate ( true )
n . MustUp ( )
n . AwaitRunning ( )
checkDefault ( n , true )
// Now we disconnect and change the server to send "false", which
// the client should ignore, having previously remembered
// "true".
n . MustDown ( )
setDefaultAutoUpdate ( false ) // control sends default "false"
n . MustUp ( )
n . AwaitRunning ( )
checkDefault ( n , true ) // still true
// But can be changed explicitly by the user.
// But can be changed explicitly by the user.
if out , err := n . TailscaleForOutput ( "set" , "--auto-update=false" ) . CombinedOutput ( ) ; err != nil {
if out , err := n . TailscaleForOutput ( "set" , "--auto-update=false" ) . CombinedOutput ( ) ; err != nil {
t . Fatalf ( "failed to disable auto-update on node: %v\noutput: %s" , err , out )
t . Fatalf ( "failed to en able auto-update on node: %v\noutput: %s", err , out )
}
}
sendAndCheckDefault ( t , n , true , false )
checkDefault( n , false )
} ,
} ,
} ,
} ,
{
{
@ -1484,22 +1529,21 @@ func TestAutoUpdateDefaults(t *testing.T) {
if out , err := n . TailscaleForOutput ( "set" , "--auto-update=false" ) . CombinedOutput ( ) ; err != nil {
if out , err := n . TailscaleForOutput ( "set" , "--auto-update=false" ) . CombinedOutput ( ) ; err != nil {
t . Fatalf ( "failed to disable auto-update on node: %v\noutput: %s" , err , out )
t . Fatalf ( "failed to disable auto-update on node: %v\noutput: %s" , err , out )
}
}
// Defaults sent from control should be ignored.
sendAndCheckDefault ( t , n , true , false )
setDefaultAutoUpdate ( true )
sendAndCheckDefault ( t , n , false , false )
n . MustUp ( )
n . AwaitRunning ( )
checkDefault ( n , false )
} ,
} ,
} ,
} ,
}
}
for _ , tt := range tests {
for _ , tt := range tests {
t . Run ( tt . desc , func ( t * testing . T ) {
t . Run ( tt . desc , func ( t * testing . T ) {
n := NewTestNode ( t , env )
n := NewTestNode ( t , env )
n . allowUpdates = true
d := n . StartDaemon ( )
d := n . StartDaemon ( )
defer d . MustCleanShutdown ( t )
defer d . MustCleanShutdown ( t )
n . AwaitResponding ( )
n . AwaitResponding ( )
n . MustUp ( )
n . AwaitRunning ( )
tt . run ( t , n )
tt . run ( t , n )
} )
} )
}
}