Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit421c0d1

Browse files
authored
chore: add nginx topology to tailnet tests (#13188)
1 parent677be9a commit421c0d1

File tree

6 files changed

+253
-167
lines changed

6 files changed

+253
-167
lines changed

‎.github/workflows/ci.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ jobs:
410410
-name:Setup Go
411411
uses:./.github/actions/setup-go
412412

413+
# Used by some integration tests.
414+
-name:Install Nginx
415+
run:sudo apt-get update && sudo apt-get install -y nginx
416+
413417
-name:Run Tests
414418
run:make test-tailnet-integration
415419

‎codersdk/organizations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func ProvisionerTypeValid[T ProvisionerType | string](pt T) error {
3434
casestring(ProvisionerTypeEcho),string(ProvisionerTypeTerraform):
3535
returnnil
3636
default:
37-
returnfmt.Errorf("provisioner type '%s' is not supported",pt)
37+
returnxerrors.Errorf("provisioner type '%s' is not supported",pt)
3838
}
3939
}
4040

‎provisionerd/provisionerd_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ func TestProvisionerd(t *testing.T) {
611611
server:=createProvisionerd(t,func(ctx context.Context) (proto.DRPCProvisionerDaemonClient,error) {
612612
// This is the dial out to Coderd, which in this unit test will always fail.
613613
connectAttemptedClose.Do(func() {close(connectAttempted) })
614-
returnnil,fmt.Errorf("client connection always fails")
614+
returnnil,xerrors.New("client connection always fails")
615615
}, provisionerd.LocalProvisioners{
616616
"someprovisioner":createProvisionerClient(t,done,provisionerTestServer{}),
617617
})

‎tailnet/test/integration/integration.go

Lines changed: 230 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import (
77
"context"
88
"fmt"
99
"io"
10+
"net"
1011
"net/http"
1112
"net/netip"
1213
"net/url"
14+
"os"
15+
"os/exec"
16+
"path/filepath"
1317
"strconv"
1418
"strings"
19+
"sync"
1520
"sync/atomic"
21+
"syscall"
1622
"testing"
1723
"time"
1824

@@ -41,7 +47,34 @@ var (
4147
Client2ID=uuid.MustParse("00000000-0000-0000-0000-000000000002")
4248
)
4349

44-
typeServerOptionsstruct {
50+
typeTestTopologystruct {
51+
Namestring
52+
// SetupNetworking creates interfaces and network namespaces for the test.
53+
// The most simple implementation is NetworkSetupDefault, which only creates
54+
// a network namespace shared for all tests.
55+
SetupNetworkingfunc(t*testing.T,logger slog.Logger)TestNetworking
56+
57+
// Server is the server starter for the test. It is executed in the server
58+
// subprocess.
59+
ServerServerStarter
60+
// StartClient gets called in each client subprocess. It's expected to
61+
// create the tailnet.Conn and ensure connectivity to it's peer.
62+
StartClientfunc(t*testing.T,logger slog.Logger,serverURL*url.URL,myID uuid.UUID,peerID uuid.UUID)*tailnet.Conn
63+
64+
// RunTests is the main test function. It's called in each of the client
65+
// subprocesses. If tests can only run once, they should check the client ID
66+
// and return early if it's not the expected one.
67+
RunTestsfunc(t*testing.T,logger slog.Logger,serverURL*url.URL,myID uuid.UUID,peerID uuid.UUID,conn*tailnet.Conn)
68+
}
69+
70+
typeServerStarterinterface {
71+
// StartServer should start the server and return once it's listening. It
72+
// should not block once it's listening. Cleanup should be handled by
73+
// t.Cleanup.
74+
StartServer(t*testing.T,logger slog.Logger,listenAddrstring)
75+
}
76+
77+
typeSimpleServerOptionsstruct {
4578
// FailUpgradeDERP will make the DERP server fail to handle the initial DERP
4679
// upgrade in a way that causes the client to fallback to
4780
// DERP-over-WebSocket fallback automatically.
@@ -54,8 +87,10 @@ type ServerOptions struct {
5487
DERPWebsocketOnlybool
5588
}
5689

90+
var_ServerStarter=SimpleServerOptions{}
91+
5792
//nolint:revive
58-
func (oServerOptions)Router(t*testing.T,logger slog.Logger)*chi.Mux {
93+
func (oSimpleServerOptions)Router(t*testing.T,logger slog.Logger)*chi.Mux {
5994
coord:=tailnet.NewCoordinator(logger)
6095
varcoordPtr atomic.Pointer[tailnet.Coordinator]
6196
coordPtr.Store(&coord)
@@ -157,6 +192,76 @@ func (o ServerOptions) Router(t *testing.T, logger slog.Logger) *chi.Mux {
157192
returnr
158193
}
159194

195+
func (oSimpleServerOptions)StartServer(t*testing.T,logger slog.Logger,listenAddrstring) {
196+
srv:= http.Server{
197+
Addr:listenAddr,
198+
Handler:o.Router(t,logger),
199+
ReadTimeout:10*time.Second,
200+
}
201+
serveDone:=make(chanstruct{})
202+
gofunc() {
203+
deferclose(serveDone)
204+
err:=srv.ListenAndServe()
205+
iferr!=nil&&!xerrors.Is(err,http.ErrServerClosed) {
206+
t.Error("HTTP server error:",err)
207+
}
208+
}()
209+
t.Cleanup(func() {
210+
_=srv.Close()
211+
<-serveDone
212+
})
213+
}
214+
215+
typeNGINXServerOptionsstruct {
216+
SimpleServerOptions
217+
}
218+
219+
var_ServerStarter=NGINXServerOptions{}
220+
221+
func (oNGINXServerOptions)StartServer(t*testing.T,logger slog.Logger,listenAddrstring) {
222+
host,nginxPortStr,err:=net.SplitHostPort(listenAddr)
223+
require.NoError(t,err)
224+
225+
nginxPort,err:=strconv.Atoi(nginxPortStr)
226+
require.NoError(t,err)
227+
228+
serverPort:=nginxPort+1
229+
serverListenAddr:=net.JoinHostPort(host,strconv.Itoa(serverPort))
230+
231+
o.SimpleServerOptions.StartServer(t,logger,serverListenAddr)
232+
startNginx(t,nginxPortStr,serverListenAddr)
233+
}
234+
235+
funcstartNginx(t*testing.T,listenPort,serverAddrstring) {
236+
cfg:=`events {}
237+
http {
238+
server {
239+
listen `+listenPort+`;
240+
server_name _;
241+
location / {
242+
proxy_pass http://`+serverAddr+`;
243+
proxy_http_version 1.1;
244+
proxy_set_header Upgrade $http_upgrade;
245+
proxy_set_header Connection "upgrade";
246+
proxy_set_header Host $host;
247+
proxy_set_header X-Real-IP $remote_addr;
248+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
249+
proxy_set_header X-Forwarded-Proto $scheme;
250+
proxy_set_header X-Forwarded-Host $server_name;
251+
}
252+
}
253+
}
254+
`
255+
256+
dir:=t.TempDir()
257+
cfgPath:=filepath.Join(dir,"nginx.conf")
258+
err:=os.WriteFile(cfgPath, []byte(cfg),0o600)
259+
require.NoError(t,err)
260+
261+
// ExecBackground will handle cleanup.
262+
_,_=ExecBackground(t,"server.nginx",nil,"nginx", []string{"-c",cfgPath})
263+
}
264+
160265
// StartClientDERP creates a client connection to the server for coordination
161266
// and creates a tailnet.Conn which will only use DERP to connect to the peer.
162267
funcStartClientDERP(t*testing.T,logger slog.Logger,serverURL*url.URL,myID,peerID uuid.UUID)*tailnet.Conn {
@@ -296,3 +401,126 @@ func basicDERPMap(t *testing.T, serverURL *url.URL) *tailcfg.DERPMap {
296401
},
297402
}
298403
}
404+
405+
// ExecBackground starts a subprocess with the given flags and returns a
406+
// channel that will receive the error when the subprocess exits. The returned
407+
// function can be used to close the subprocess.
408+
//
409+
// processName is used to identify the subprocess in logs.
410+
//
411+
// Optionally, a network namespace can be passed to run the subprocess in.
412+
//
413+
// Do not call close then wait on the channel. Use the returned value from the
414+
// function instead in this case.
415+
//
416+
// Cleanup is handled automatically if you don't care about monitoring the
417+
// process manually.
418+
funcExecBackground(t*testing.T,processNamestring,netNS*os.File,namestring,args []string) (<-chanerror,func()error) {
419+
ifnetNS!=nil {
420+
// We use nsenter to enter the namespace.
421+
// We can't use `setns` easily from Golang in the parent process because
422+
// you can't execute the syscall in the forked child thread before it
423+
// execs.
424+
// We can't use `setns` easily from Golang in the child process because
425+
// by the time you call it, the process has already created multiple
426+
// threads.
427+
args=append([]string{"--net=/proc/self/fd/3",name},args...)
428+
name="nsenter"
429+
}
430+
431+
cmd:=exec.Command(name,args...)
432+
ifnetNS!=nil {
433+
cmd.ExtraFiles= []*os.File{netNS}
434+
}
435+
436+
out:=&testWriter{
437+
name:processName,
438+
t:t,
439+
}
440+
t.Cleanup(out.Flush)
441+
cmd.Stdout=out
442+
cmd.Stderr=out
443+
cmd.SysProcAttr=&syscall.SysProcAttr{
444+
Pdeathsig:syscall.SIGTERM,
445+
}
446+
err:=cmd.Start()
447+
require.NoError(t,err)
448+
449+
waitErr:=make(chanerror,1)
450+
gofunc() {
451+
err:=cmd.Wait()
452+
waitErr<-err
453+
close(waitErr)
454+
}()
455+
456+
closeFn:=func()error {
457+
_=cmd.Process.Signal(syscall.SIGTERM)
458+
select {
459+
case<-time.After(5*time.Second):
460+
_=cmd.Process.Kill()
461+
caseerr:=<-waitErr:
462+
returnerr
463+
}
464+
return<-waitErr
465+
}
466+
467+
t.Cleanup(func() {
468+
select {
469+
caseerr:=<-waitErr:
470+
iferr!=nil {
471+
t.Logf("subprocess exited: "+err.Error())
472+
}
473+
return
474+
default:
475+
}
476+
477+
_=closeFn()
478+
})
479+
480+
returnwaitErr,closeFn
481+
}
482+
483+
typetestWriterstruct {
484+
mut sync.Mutex
485+
namestring
486+
t*testing.T
487+
488+
capturedLines []string
489+
}
490+
491+
func (w*testWriter)Write(p []byte) (nint,errerror) {
492+
w.mut.Lock()
493+
deferw.mut.Unlock()
494+
str:=string(p)
495+
split:=strings.Split(str,"\n")
496+
for_,s:=rangesplit {
497+
ifs=="" {
498+
continue
499+
}
500+
501+
// If a line begins with "\s*--- (PASS|FAIL)" or is just PASS or FAIL,
502+
// then it's a test result line. We want to capture it and log it later.
503+
trimmed:=strings.TrimSpace(s)
504+
ifstrings.HasPrefix(trimmed,"--- PASS")||strings.HasPrefix(trimmed,"--- FAIL")||trimmed=="PASS"||trimmed=="FAIL" {
505+
// Also fail the test if we see a FAIL line.
506+
ifstrings.Contains(trimmed,"FAIL") {
507+
w.t.Errorf("subprocess logged test failure: %s:\t%s",w.name,s)
508+
}
509+
510+
w.capturedLines=append(w.capturedLines,s)
511+
continue
512+
}
513+
514+
w.t.Logf("%s output:\t%s",w.name,s)
515+
}
516+
returnlen(p),nil
517+
}
518+
519+
func (w*testWriter)Flush() {
520+
w.mut.Lock()
521+
deferw.mut.Unlock()
522+
for_,s:=rangew.capturedLines {
523+
w.t.Logf("%s output:\t%s",w.name,s)
524+
}
525+
w.capturedLines=nil
526+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp