@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 10, 22:30 UTC

master
go 150 lines 4.2 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddy
16
17 import (
18 "context"
19 "fmt"
20 "os"
21 "os/exec"
22 "strings"
23 "testing"
24 "time"
25
26 "github.com/caddyserver/caddy/v2/notify"
27 "golang.org/x/sys/windows/svc"
28 )
29
30 func TestRunnerStatusOrdering(t *testing.T) {
31 for _, order := range []string{"ready first", "runner first", "concurrent"} {
32 t.Run(order, func(t *testing.T) {
33 notify.SetGlobalStatus(nil)
34 t.Cleanup(func() { notify.SetGlobalStatus(nil) })
35 requests := make(chan svc.ChangeRequest)
36 statuses := make(chan svc.Status, 4)
37 done := make(chan struct{})
38 if order == "ready first" {
39 _ = notify.Ready()
40 }
41 go func() {
42 defer close(done)
43 runner{}.Execute(nil, requests, statuses)
44 }()
45 if order == "runner first" {
46 select {
47 case s := <-statuses:
48 if s.State != svc.StartPending {
49 t.Fatalf("initial status: %+v", s)
50 }
51 case <-time.After(5 * time.Second):
52 t.Fatal("runner did not start")
53 }
54 }
55 requestDone := make(chan struct{})
56 go func() {
57 defer close(requestDone)
58 defer close(requests)
59 if order != "ready first" {
60 _ = notify.Ready()
61 }
62 // The snapshot is deliberately stale: the owner must publish RUNNING.
63 requests <- svc.ChangeRequest{Cmd: svc.Interrogate, CurrentStatus: svc.Status{State: svc.StartPending}}
64 }()
65 t.Cleanup(func() {
66 select {
67 case <-done:
68 case <-time.After(5 * time.Second):
69 t.Error("runner did not return")
70 }
71 })
72 select {
73 case <-requestDone:
74 case <-time.After(5 * time.Second):
75 t.Fatal("notification or Interrogate blocked")
76 }
77 select {
78 case <-done:
79 case <-time.After(5 * time.Second):
80 t.Fatal("runner did not return")
81 }
82 close(statuses)
83 want := svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
84 var last svc.Status
85 ready := false
86 for s := range statuses {
87 ready = ready || s == want
88 if ready && s != want {
89 t.Fatalf("stale status after RUNNING: %+v", s)
90 }
91 last = s
92 }
93 if last != want {
94 t.Fatalf("last status: got %+v, want %+v", last, want)
95 }
96 returned := make(chan struct{})
97 go func() { _ = notify.Ready(); close(returned) }()
98 select {
99 case <-returned:
100 case <-time.After(5 * time.Second):
101 t.Fatal("Ready blocked after runner returned")
102 }
103 })
104 }
105 }
106
107 func TestRunnerStopRacesReady(t *testing.T) {
108 const envKey = "CADDY_TEST_SERVICE_CONTROL"
109 controls := map[string]svc.Cmd{"stop": svc.Stop, "shutdown": svc.Shutdown}
110 if name := os.Getenv(envKey); name != "" {
111 requests := make(chan svc.ChangeRequest)
112 statuses := make(chan svc.Status)
113 go runner{}.Execute(nil, requests, statuses)
114 go func() {
115 for s := range statuses {
116 fmt.Println(s.State)
117 }
118 }()
119 go func() {
120 for {
121 _ = notify.Ready()
122 }
123 }()
124 requests <- svc.ChangeRequest{Cmd: controls[name]}
125 time.Sleep(10 * time.Second)
126 os.Exit(5) // Stop/Shutdown must exit the process before this fallback.
127 }
128 for name := range controls {
129 t.Run(name, func(t *testing.T) {
130 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
131 defer cancel()
132 child := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestRunnerStopRacesReady$", "-test.count=1")
133 child.Env = append(os.Environ(), envKey+"="+name)
134 out, err := child.Output()
135 if err != nil {
136 t.Fatalf("child failed: %v; output:\n%s", err, out)
137 }
138 stopping := false
139 for line := range strings.FieldsSeq(string(out)) {
140 stopping = stopping || line == fmt.Sprint(svc.StopPending)
141 if stopping && line == fmt.Sprint(svc.Running) {
142 t.Fatalf("RUNNING after STOP_PENDING:\n%s", out)
143 }
144 }
145 if !stopping {
146 t.Fatalf("STOP_PENDING missing:\n%s", out)
147 }
148 })
149 }
150 }