@@ -18,11 +18,16 @@ package filters
18
18
19
19
import (
20
20
"errors"
21
+ "fmt"
21
22
"net/http"
22
23
24
+ "k8s.io/api/core/v1"
25
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
26
+ "k8s.io/apimachinery/pkg/runtime"
23
27
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
24
28
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
25
29
apirequest "k8s.io/apiserver/pkg/endpoints/request"
30
+ "k8s.io/client-go/kubernetes/scheme"
26
31
)
27
32
28
33
// WithWaitGroup adds all non long-running requests to wait group, which is used for graceful shutdown.
@@ -38,7 +43,14 @@ func WithWaitGroup(handler http.Handler, longRunning apirequest.LongRunningReque
38
43
39
44
if ! longRunning (req , requestInfo ) {
40
45
if err := wg .Add (1 ); err != nil {
41
- http .Error (w , "apiserver is shutting down." , http .StatusInternalServerError )
46
+ // When apiserver is shutting down, signal clients to retry
47
+ // There is a good chance the client hit a different server, so a tight retry is good for client responsiveness.
48
+ w .Header ().Add ("Retry-After" , "1" )
49
+ w .Header ().Set ("Content-Type" , runtime .ContentTypeJSON )
50
+ w .Header ().Set ("X-Content-Type-Options" , "nosniff" )
51
+ statusErr := apierrors .NewServiceUnavailable ("apiserver is shutting down" ).Status ()
52
+ w .WriteHeader (int (statusErr .Code ))
53
+ fmt .Fprintln (w , runtime .EncodeOrDie (scheme .Codecs .LegacyCodec (v1 .SchemeGroupVersion ), & statusErr ))
42
54
return
43
55
}
44
56
defer wg .Done ()
0 commit comments