Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
97727267
Commit
97727267
authored
May 22, 2015
by
Dawn Chen
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8574 from bprashanth/apiserver_rand
Randomize apiserver timeout
parents
1131ac9a
8a5445d3
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
131 additions
and
13 deletions
+131
-13
apiserver.go
cmd/kube-apiserver/apiserver.go
+4
-0
server.go
cmd/kube-apiserver/app/server.go
+12
-7
apiserver.go
pkg/apiserver/apiserver.go
+8
-0
watch.go
pkg/apiserver/watch.go
+38
-6
watch_test.go
pkg/apiserver/watch_test.go
+69
-0
No files found.
cmd/kube-apiserver/apiserver.go
View file @
97727267
...
@@ -20,8 +20,10 @@ package main
...
@@ -20,8 +20,10 @@ package main
import
(
import
(
"fmt"
"fmt"
"math/rand"
"os"
"os"
"runtime"
"runtime"
"time"
"github.com/GoogleCloudPlatform/kubernetes/cmd/kube-apiserver/app"
"github.com/GoogleCloudPlatform/kubernetes/cmd/kube-apiserver/app"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
...
@@ -32,6 +34,8 @@ import (
...
@@ -32,6 +34,8 @@ import (
func
main
()
{
func
main
()
{
runtime
.
GOMAXPROCS
(
runtime
.
NumCPU
())
runtime
.
GOMAXPROCS
(
runtime
.
NumCPU
())
rand
.
Seed
(
time
.
Now
()
.
UTC
()
.
UnixNano
())
s
:=
app
.
NewAPIServer
()
s
:=
app
.
NewAPIServer
()
s
.
AddFlags
(
pflag
.
CommandLine
)
s
.
AddFlags
(
pflag
.
CommandLine
)
...
...
cmd/kube-apiserver/app/server.go
View file @
97727267
...
@@ -46,6 +46,12 @@ import (
...
@@ -46,6 +46,12 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/pflag"
)
)
const
(
// Maximum duration before timing out read/write requests
// Set to a value larger than the timeouts in each watch server.
ReadWriteTimeout
=
time
.
Minute
*
60
)
// APIServer runs a kubernetes api server.
// APIServer runs a kubernetes api server.
type
APIServer
struct
{
type
APIServer
struct
{
InsecureBindAddress
util
.
IP
InsecureBindAddress
util
.
IP
...
@@ -393,8 +399,8 @@ func (s *APIServer) Run(_ []string) error {
...
@@ -393,8 +399,8 @@ func (s *APIServer) Run(_ []string) error {
readOnlyServer
:=
&
http
.
Server
{
readOnlyServer
:=
&
http
.
Server
{
Addr
:
roLocation
,
Addr
:
roLocation
,
Handler
:
apiserver
.
MaxInFlightLimit
(
sem
,
longRunningRE
,
apiserver
.
RecoverPanics
(
apiserver
.
ReadOnly
(
apiserver
.
RateLimit
(
rl
,
m
.
InsecureHandler
)))),
Handler
:
apiserver
.
MaxInFlightLimit
(
sem
,
longRunningRE
,
apiserver
.
RecoverPanics
(
apiserver
.
ReadOnly
(
apiserver
.
RateLimit
(
rl
,
m
.
InsecureHandler
)))),
ReadTimeout
:
5
*
time
.
Minute
,
ReadTimeout
:
ReadWriteTimeout
,
WriteTimeout
:
5
*
time
.
Minute
,
WriteTimeout
:
ReadWriteTimeout
,
MaxHeaderBytes
:
1
<<
20
,
MaxHeaderBytes
:
1
<<
20
,
}
}
glog
.
Infof
(
"Serving read-only insecurely on %s"
,
roLocation
)
glog
.
Infof
(
"Serving read-only insecurely on %s"
,
roLocation
)
...
@@ -413,8 +419,8 @@ func (s *APIServer) Run(_ []string) error {
...
@@ -413,8 +419,8 @@ func (s *APIServer) Run(_ []string) error {
secureServer
:=
&
http
.
Server
{
secureServer
:=
&
http
.
Server
{
Addr
:
secureLocation
,
Addr
:
secureLocation
,
Handler
:
apiserver
.
MaxInFlightLimit
(
sem
,
longRunningRE
,
apiserver
.
RecoverPanics
(
m
.
Handler
)),
Handler
:
apiserver
.
MaxInFlightLimit
(
sem
,
longRunningRE
,
apiserver
.
RecoverPanics
(
m
.
Handler
)),
ReadTimeout
:
5
*
time
.
Minute
,
ReadTimeout
:
ReadWriteTimeout
,
WriteTimeout
:
5
*
time
.
Minute
,
WriteTimeout
:
ReadWriteTimeout
,
MaxHeaderBytes
:
1
<<
20
,
MaxHeaderBytes
:
1
<<
20
,
TLSConfig
:
&
tls
.
Config
{
TLSConfig
:
&
tls
.
Config
{
// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
...
@@ -454,12 +460,11 @@ func (s *APIServer) Run(_ []string) error {
...
@@ -454,12 +460,11 @@ func (s *APIServer) Run(_ []string) error {
}
}
}()
}()
}
}
http
:=
&
http
.
Server
{
http
:=
&
http
.
Server
{
Addr
:
insecureLocation
,
Addr
:
insecureLocation
,
Handler
:
apiserver
.
RecoverPanics
(
m
.
InsecureHandler
),
Handler
:
apiserver
.
RecoverPanics
(
m
.
InsecureHandler
),
ReadTimeout
:
5
*
time
.
Minute
,
ReadTimeout
:
ReadWriteTimeout
,
WriteTimeout
:
5
*
time
.
Minute
,
WriteTimeout
:
ReadWriteTimeout
,
MaxHeaderBytes
:
1
<<
20
,
MaxHeaderBytes
:
1
<<
20
,
}
}
glog
.
Infof
(
"Serving insecurely on %s"
,
insecureLocation
)
glog
.
Infof
(
"Serving insecurely on %s"
,
insecureLocation
)
...
...
pkg/apiserver/apiserver.go
View file @
97727267
...
@@ -132,6 +132,14 @@ type APIGroupVersion struct {
...
@@ -132,6 +132,14 @@ type APIGroupVersion struct {
Context
api
.
RequestContextMapper
Context
api
.
RequestContextMapper
}
}
// TODO: Pipe these in through the apiserver cmd line
const
(
// Minimum duration before timing out read/write requests
MinTimeoutSecs
=
300
// Maximum duration before timing out read/write requests
MaxTimeoutSecs
=
600
)
// InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container.
// InstallREST registers the REST handlers (storage, watch, proxy and redirect) into a restful Container.
// It is expected that the provided path root prefix will serve all operations. Root MUST NOT end
// It is expected that the provided path root prefix will serve all operations. Root MUST NOT end
// in a slash. A restful WebService is created for the group and version.
// in a slash. A restful WebService is created for the group and version.
...
...
pkg/apiserver/watch.go
View file @
97727267
...
@@ -17,10 +17,12 @@ limitations under the License.
...
@@ -17,10 +17,12 @@ limitations under the License.
package
apiserver
package
apiserver
import
(
import
(
"math/rand"
"net/http"
"net/http"
"reflect"
"reflect"
"regexp"
"regexp"
"strings"
"strings"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
...
@@ -32,19 +34,47 @@ import (
...
@@ -32,19 +34,47 @@ import (
"golang.org/x/net/websocket"
"golang.org/x/net/websocket"
)
)
var
connectionUpgradeRegex
=
regexp
.
MustCompile
(
"(^|.*,
\\
s*)upgrade($|
\\
s*,)"
)
var
(
connectionUpgradeRegex
=
regexp
.
MustCompile
(
"(^|.*,
\\
s*)upgrade($|
\\
s*,)"
)
// nothing will ever be sent down this channel
neverExitWatch
<-
chan
time
.
Time
=
make
(
chan
time
.
Time
)
)
func
isWebsocketRequest
(
req
*
http
.
Request
)
bool
{
func
isWebsocketRequest
(
req
*
http
.
Request
)
bool
{
return
connectionUpgradeRegex
.
MatchString
(
strings
.
ToLower
(
req
.
Header
.
Get
(
"Connection"
)))
&&
strings
.
ToLower
(
req
.
Header
.
Get
(
"Upgrade"
))
==
"websocket"
return
connectionUpgradeRegex
.
MatchString
(
strings
.
ToLower
(
req
.
Header
.
Get
(
"Connection"
)))
&&
strings
.
ToLower
(
req
.
Header
.
Get
(
"Upgrade"
))
==
"websocket"
}
}
// timeoutFactory abstracts watch timeout logic for testing
type
timeoutFactory
interface
{
TimeoutCh
()
(
<-
chan
time
.
Time
,
func
()
bool
)
}
// realTimeoutFactory implements timeoutFactory
type
realTimeoutFactory
struct
{
timeout
time
.
Duration
}
// TimeoutChan returns a channel which will receive something when the watch times out,
// and a cleanup function to call when this happens.
func
(
w
*
realTimeoutFactory
)
TimeoutCh
()
(
<-
chan
time
.
Time
,
func
()
bool
)
{
if
w
.
timeout
==
0
{
return
neverExitWatch
,
func
()
bool
{
return
false
}
}
t
:=
time
.
NewTimer
(
w
.
timeout
)
return
t
.
C
,
t
.
Stop
}
// serveWatch handles serving requests to the server
// serveWatch handles serving requests to the server
func
serveWatch
(
watcher
watch
.
Interface
,
scope
RequestScope
,
w
http
.
ResponseWriter
,
req
*
restful
.
Request
)
{
func
serveWatch
(
watcher
watch
.
Interface
,
scope
RequestScope
,
w
http
.
ResponseWriter
,
req
*
restful
.
Request
)
{
// Each watch gets a random timeout to avoid thundering herds. Rand is seeded once in the api installer.
timeout
:=
time
.
Duration
(
MinTimeoutSecs
+
rand
.
Intn
(
MaxTimeoutSecs
-
MinTimeoutSecs
))
*
time
.
Second
watchServer
:=
&
WatchServer
{
watcher
,
scope
.
Codec
,
func
(
obj
runtime
.
Object
)
{
watchServer
:=
&
WatchServer
{
watcher
,
scope
.
Codec
,
func
(
obj
runtime
.
Object
)
{
if
err
:=
setSelfLink
(
obj
,
req
,
scope
.
Namer
);
err
!=
nil
{
if
err
:=
setSelfLink
(
obj
,
req
,
scope
.
Namer
);
err
!=
nil
{
glog
.
V
(
5
)
.
Infof
(
"Failed to set self link for object %v: %v"
,
reflect
.
TypeOf
(
obj
),
err
)
glog
.
V
(
5
)
.
Infof
(
"Failed to set self link for object %v: %v"
,
reflect
.
TypeOf
(
obj
),
err
)
}
}
}}
}
,
&
realTimeoutFactory
{
timeout
}
}
if
isWebsocketRequest
(
req
.
Request
)
{
if
isWebsocketRequest
(
req
.
Request
)
{
websocket
.
Handler
(
watchServer
.
HandleWS
)
.
ServeHTTP
(
httplog
.
Unlogged
(
w
),
req
.
Request
)
websocket
.
Handler
(
watchServer
.
HandleWS
)
.
ServeHTTP
(
httplog
.
Unlogged
(
w
),
req
.
Request
)
}
else
{
}
else
{
...
@@ -57,6 +87,7 @@ type WatchServer struct {
...
@@ -57,6 +87,7 @@ type WatchServer struct {
watching
watch
.
Interface
watching
watch
.
Interface
codec
runtime
.
Codec
codec
runtime
.
Codec
fixup
func
(
runtime
.
Object
)
fixup
func
(
runtime
.
Object
)
t
timeoutFactory
}
}
// HandleWS implements a websocket handler.
// HandleWS implements a websocket handler.
...
@@ -100,6 +131,9 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) {
...
@@ -100,6 +131,9 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) {
func
(
self
*
WatchServer
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
func
(
self
*
WatchServer
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
loggedW
:=
httplog
.
LogOf
(
req
,
w
)
loggedW
:=
httplog
.
LogOf
(
req
,
w
)
w
=
httplog
.
Unlogged
(
w
)
w
=
httplog
.
Unlogged
(
w
)
timeoutCh
,
cleanup
:=
self
.
t
.
TimeoutCh
()
defer
cleanup
()
defer
self
.
watching
.
Stop
()
cn
,
ok
:=
w
.
(
http
.
CloseNotifier
)
cn
,
ok
:=
w
.
(
http
.
CloseNotifier
)
if
!
ok
{
if
!
ok
{
...
@@ -113,16 +147,15 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...
@@ -113,16 +147,15 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http
.
NotFound
(
w
,
req
)
http
.
NotFound
(
w
,
req
)
return
return
}
}
w
.
Header
()
.
Set
(
"Transfer-Encoding"
,
"chunked"
)
w
.
Header
()
.
Set
(
"Transfer-Encoding"
,
"chunked"
)
w
.
WriteHeader
(
http
.
StatusOK
)
w
.
WriteHeader
(
http
.
StatusOK
)
flusher
.
Flush
()
flusher
.
Flush
()
encoder
:=
watchjson
.
NewEncoder
(
w
,
self
.
codec
)
encoder
:=
watchjson
.
NewEncoder
(
w
,
self
.
codec
)
for
{
for
{
select
{
select
{
case
<-
cn
.
CloseNotify
()
:
case
<-
cn
.
CloseNotify
()
:
self
.
watching
.
Stop
()
return
case
<-
timeoutCh
:
return
return
case
event
,
ok
:=
<-
self
.
watching
.
ResultChan
()
:
case
event
,
ok
:=
<-
self
.
watching
.
ResultChan
()
:
if
!
ok
{
if
!
ok
{
...
@@ -132,7 +165,6 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...
@@ -132,7 +165,6 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
self
.
fixup
(
event
.
Object
)
self
.
fixup
(
event
.
Object
)
if
err
:=
encoder
.
Encode
(
&
event
);
err
!=
nil
{
if
err
:=
encoder
.
Encode
(
&
event
);
err
!=
nil
{
// Client disconnect.
// Client disconnect.
self
.
watching
.
Stop
()
return
return
}
}
flusher
.
Flush
()
flusher
.
Flush
()
...
...
pkg/apiserver/watch_test.go
View file @
97727267
...
@@ -18,11 +18,13 @@ package apiserver
...
@@ -18,11 +18,13 @@ package apiserver
import
(
import
(
"encoding/json"
"encoding/json"
"io"
"net/http"
"net/http"
"net/http/httptest"
"net/http/httptest"
"net/url"
"net/url"
"reflect"
"reflect"
"testing"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
...
@@ -278,4 +280,71 @@ func TestWatchProtocolSelection(t *testing.T) {
...
@@ -278,4 +280,71 @@ func TestWatchProtocolSelection(t *testing.T) {
t
.
Errorf
(
"Unexpected response %#v"
,
response
)
t
.
Errorf
(
"Unexpected response %#v"
,
response
)
}
}
}
}
}
type
fakeTimeoutFactory
struct
{
timeoutCh
chan
time
.
Time
done
chan
struct
{}
}
func
(
t
*
fakeTimeoutFactory
)
TimeoutCh
()
(
<-
chan
time
.
Time
,
func
()
bool
)
{
return
t
.
timeoutCh
,
func
()
bool
{
defer
close
(
t
.
done
)
return
true
}
}
func
TestWatchHTTPTimeout
(
t
*
testing
.
T
)
{
watcher
:=
watch
.
NewFake
()
timeoutCh
:=
make
(
chan
time
.
Time
)
done
:=
make
(
chan
struct
{})
// Setup a new watchserver
watchServer
:=
&
WatchServer
{
watcher
,
version2ServerCodec
,
func
(
obj
runtime
.
Object
)
{},
&
fakeTimeoutFactory
{
timeoutCh
,
done
},
}
s
:=
httptest
.
NewServer
(
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
watchServer
.
ServeHTTP
(
w
,
req
)
}))
defer
s
.
Close
()
// Setup a client
dest
,
_
:=
url
.
Parse
(
s
.
URL
)
dest
.
Path
=
"/api/version/watch/resource"
dest
.
RawQuery
=
""
req
,
_
:=
http
.
NewRequest
(
"GET"
,
dest
.
String
(),
nil
)
client
:=
http
.
Client
{}
resp
,
err
:=
client
.
Do
(
req
)
watcher
.
Add
(
&
api
.
Pod
{
TypeMeta
:
api
.
TypeMeta
{
APIVersion
:
"v1beta3"
}})
// Make sure we can actually watch an endpoint
decoder
:=
json
.
NewDecoder
(
resp
.
Body
)
var
got
watchJSON
err
=
decoder
.
Decode
(
&
got
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
// Timeout and check for leaks
close
(
timeoutCh
)
select
{
case
<-
done
:
if
!
watcher
.
Stopped
{
t
.
Errorf
(
"Leaked watch on timeout"
)
}
case
<-
time
.
After
(
100
*
time
.
Millisecond
)
:
t
.
Errorf
(
"Failed to stop watcher after 100ms of timeout signal"
)
}
// Make sure we can't receive any more events through the timeout watch
err
=
decoder
.
Decode
(
&
got
)
if
err
!=
io
.
EOF
{
t
.
Errorf
(
"Unexpected non-error"
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment