From 42994faf18d39b8940eb0f05ca04e5cda984d89f Mon Sep 17 00:00:00 2001 From: Csaba Sarkadi Date: Tue, 19 Nov 2024 18:36:57 +0100 Subject: [PATCH 1/5] integration testing: add and validate build-time options for tailscale head --- Dockerfile.tailscale-HEAD | 4 ++- integration/embedded_derp_test.go | 7 +++-- integration/tsic/tsic.go | 49 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Dockerfile.tailscale-HEAD b/Dockerfile.tailscale-HEAD index 92b0cae570..82f7a8d920 100644 --- a/Dockerfile.tailscale-HEAD +++ b/Dockerfile.tailscale-HEAD @@ -28,7 +28,9 @@ ARG VERSION_GIT_HASH="" ENV VERSION_GIT_HASH=$VERSION_GIT_HASH ARG TARGETARCH -RUN GOARCH=$TARGETARCH go install -ldflags="\ +ARG BUILD_TAGS="" + +RUN GOARCH=$TARGETARCH go install -tags="${BUILD_TAGS}" -ldflags="\ -X tailscale.com/version.longStamp=$VERSION_LONG \ -X tailscale.com/version.shortStamp=$VERSION_SHORT \ -X tailscale.com/version.gitCommitStamp=$VERSION_GIT_HASH" \ diff --git a/integration/embedded_derp_test.go b/integration/embedded_derp_test.go index 6009aed50d..7f73bd3846 100644 --- a/integration/embedded_derp_test.go +++ b/integration/embedded_derp_test.go @@ -55,7 +55,7 @@ func TestDERPServerWebsocketScenario(t *testing.T) { spec := map[string]ClientsSpec{ "user1": { Plain: 0, - WebsocketDERP: len(MustTestVersions), + WebsocketDERP: 2, }, } @@ -239,10 +239,13 @@ func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv( if clientCount.WebsocketDERP > 0 { // Containers that use DERP-over-WebSocket + // Note that these clients *must* be built + // from source, which is currently + // only done for HEAD. err = s.CreateTailscaleIsolatedNodesInUser( hash, userName, - "all", + "head", clientCount.WebsocketDERP, tsic.WithWebsocketDERP(true), ) diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index 944bb94dff..ed39170edc 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -12,6 +12,7 @@ import ( "net/netip" "net/url" "os" + "reflect" "strconv" "strings" "time" @@ -44,6 +45,7 @@ var ( errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey") errTailscaleNotConnected = errors.New("tailscale not connected") errTailscaledNotReadyForLogin = errors.New("tailscaled not ready for login") + errInvalidClientConfig = errors.New("verifiably invalid client config requested") ) func errTailscaleStatus(hostname string, err error) error { @@ -74,6 +76,13 @@ type TailscaleInContainer struct { withExtraHosts []string workdir string netfilter string + + // build options, solely for HEAD + buildConfig TailscaleInContainerBuildConfig +} + +type TailscaleInContainerBuildConfig struct { + tags []string } // Option represent optional settings that can be given to a @@ -175,6 +184,18 @@ func WithNetfilter(state string) Option { } } +// WithBuildTag adds an additional value to the `-tags=` parameter +// of the Go compiler, allowing callers to customize the Tailscale client build. +// This option is only meaningful when invoked on **HEAD** versions of the client. +// Attempts to use it with any other version is a bug in the calling code. +func WithBuildTag(tag string) Option { + return func(tsic *TailscaleInContainer) { + tsic.buildConfig.tags = append( + tsic.buildConfig.tags, tag, + ) + } +} + // New returns a new TailscaleInContainer instance. func New( pool *dockertest.Pool, @@ -219,6 +240,12 @@ func New( } if tsic.withWebsocketDERP { + if version != "head" { + return nil, errInvalidClientConfig + } + + WithBuildTag("ts_debug_websockets")(tsic) + tailscaleOptions.Env = append( tailscaleOptions.Env, fmt.Sprintf("TS_DEBUG_DERP_WS_CLIENT=%t", tsic.withWebsocketDERP), @@ -245,6 +272,17 @@ func New( } var container *dockertest.Resource + + if version != "head" { + // build options are not meaningful with pre-existing images, + // let's not lead anyone astray by pretending otherwise. + defaultBuildConfig := TailscaleInContainerBuildConfig{} + hasBuildConfig := reflect.DeepEqual(defaultBuildConfig, tsic.buildConfig) + if hasBuildConfig { + return nil, errInvalidClientConfig + } + } + switch version { case "head": buildOptions := &dockertest.BuildOptions{ @@ -253,6 +291,17 @@ func New( BuildArgs: []docker.BuildArg{}, } + buildTags := strings.Join(tsic.buildConfig.tags, ",") + if len(buildTags) > 0 { + buildOptions.BuildArgs = append( + buildOptions.BuildArgs, + docker.BuildArg{ + Name: "BUILD_TAGS", + Value: buildTags, + }, + ) + } + container, err = pool.BuildAndRunWithBuildOptions( buildOptions, tailscaleOptions, From d6662d50e5c0ff80638ccc31dc58b73bbc1537e3 Mon Sep 17 00:00:00 2001 From: Csaba Sarkadi Date: Tue, 19 Nov 2024 18:50:15 +0100 Subject: [PATCH 2/5] fixup! integration testing: add and validate build-time options for tailscale head integration testing: comply with linter --- integration/tsic/tsic.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index ed39170edc..ac297667a1 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -48,6 +48,10 @@ var ( errInvalidClientConfig = errors.New("verifiably invalid client config requested") ) +const ( + VersionHead = "head" +) + func errTailscaleStatus(hostname string, err error) error { return fmt.Errorf("%s failed to fetch tailscale status: %w", hostname, err) } @@ -240,7 +244,7 @@ func New( } if tsic.withWebsocketDERP { - if version != "head" { + if version != VersionHead { return nil, errInvalidClientConfig } @@ -273,7 +277,7 @@ func New( var container *dockertest.Resource - if version != "head" { + if version != VersionHead { // build options are not meaningful with pre-existing images, // let's not lead anyone astray by pretending otherwise. defaultBuildConfig := TailscaleInContainerBuildConfig{} @@ -284,7 +288,7 @@ func New( } switch version { - case "head": + case VersionHead: buildOptions := &dockertest.BuildOptions{ Dockerfile: "Dockerfile.tailscale-HEAD", ContextDir: dockerContextPath, From 2bba07d41ce68ea5b03659a799bcf5df0daf10ae Mon Sep 17 00:00:00 2001 From: Csaba Sarkadi Date: Tue, 19 Nov 2024 18:53:53 +0100 Subject: [PATCH 3/5] fixup! fixup! integration testing: add and validate build-time options for tailscale head integration testing: tsic.New must never return nil --- integration/tsic/tsic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index ac297667a1..0d1639f979 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -245,7 +245,7 @@ func New( if tsic.withWebsocketDERP { if version != VersionHead { - return nil, errInvalidClientConfig + return tsic, errInvalidClientConfig } WithBuildTag("ts_debug_websockets")(tsic) @@ -283,7 +283,7 @@ func New( defaultBuildConfig := TailscaleInContainerBuildConfig{} hasBuildConfig := reflect.DeepEqual(defaultBuildConfig, tsic.buildConfig) if hasBuildConfig { - return nil, errInvalidClientConfig + return tsic, errInvalidClientConfig } } From 43098526d2cab1ddc50fb6ba3faee35b4f97bbd5 Mon Sep 17 00:00:00 2001 From: Csaba Sarkadi Date: Tue, 19 Nov 2024 19:04:17 +0100 Subject: [PATCH 4/5] fixup! fixup! fixup! integration testing: add and validate build-time options for tailscale head --- integration/tsic/tsic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index 0d1639f979..bfaa70b609 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -281,7 +281,7 @@ func New( // build options are not meaningful with pre-existing images, // let's not lead anyone astray by pretending otherwise. defaultBuildConfig := TailscaleInContainerBuildConfig{} - hasBuildConfig := reflect.DeepEqual(defaultBuildConfig, tsic.buildConfig) + hasBuildConfig := !reflect.DeepEqual(defaultBuildConfig, tsic.buildConfig) if hasBuildConfig { return tsic, errInvalidClientConfig } From fcad6b410fbabcc3c68d9a825067ae181b078fd1 Mon Sep 17 00:00:00 2001 From: Csaba Sarkadi Date: Thu, 21 Nov 2024 18:45:50 +0100 Subject: [PATCH 5/5] minor fixes --- integration/embedded_derp_test.go | 2 +- integration/tsic/tsic.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/integration/embedded_derp_test.go b/integration/embedded_derp_test.go index 7f73bd3846..046f3890c3 100644 --- a/integration/embedded_derp_test.go +++ b/integration/embedded_derp_test.go @@ -245,7 +245,7 @@ func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv( err = s.CreateTailscaleIsolatedNodesInUser( hash, userName, - "head", + tsic.VersionHead, clientCount.WebsocketDERP, tsic.WithWebsocketDERP(true), ) diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index bfaa70b609..b0bd7a6031 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -194,6 +194,10 @@ func WithNetfilter(state string) Option { // Attempts to use it with any other version is a bug in the calling code. func WithBuildTag(tag string) Option { return func(tsic *TailscaleInContainer) { + if tsic.version != VersionHead { + panic(errInvalidClientConfig) + } + tsic.buildConfig.tags = append( tsic.buildConfig.tags, tag, )