mirror of https://github.com/k3s-io/k3s
commit
e8686429c4
|
@ -49,6 +49,7 @@ import (
|
|||
var (
|
||||
port = flag.Uint("port", 8080, "The port to listen on. Default 8080")
|
||||
address = util.IP(net.ParseIP("127.0.0.1"))
|
||||
readOnlyPort = flag.Uint("read_only_port", 7080, "The port from which to serve read-only resources. If 0, don't serve on a read-only address.")
|
||||
apiPrefix = flag.String("api_prefix", "/api", "The prefix for API requests on the server. Default '/api'.")
|
||||
storageVersion = flag.String("storage_version", "", "The version to store resources with. Defaults to server preferred")
|
||||
cloudProvider = flag.String("cloud_provider", "", "The provider for cloud services. Empty string for no provider.")
|
||||
|
@ -230,11 +231,25 @@ func main() {
|
|||
handler = handlers.NewRequestAuthenticator(userContexts, bearertoken.New(auth), handlers.Unauthorized, handler)
|
||||
}
|
||||
|
||||
handler = apiserver.RecoverPanics(handler)
|
||||
if *readOnlyPort != 0 {
|
||||
// Allow 1 read-only request per second, allow up to 20 in a burst before enforcing.
|
||||
rl := util.NewTokenBucketRateLimiter(1.0, 20)
|
||||
readOnlyServer := &http.Server{
|
||||
Addr: net.JoinHostPort(address.String(), strconv.Itoa(int(*readOnlyPort))),
|
||||
Handler: apiserver.RecoverPanics(apiserver.ReadOnly(apiserver.RateLimit(rl, handler))),
|
||||
ReadTimeout: 5 * time.Minute,
|
||||
WriteTimeout: 5 * time.Minute,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
go func() {
|
||||
defer util.HandleCrash()
|
||||
glog.Fatal(readOnlyServer.ListenAndServe())
|
||||
}()
|
||||
}
|
||||
|
||||
s := &http.Server{
|
||||
Addr: net.JoinHostPort(address.String(), strconv.Itoa(int(*port))),
|
||||
Handler: handler,
|
||||
Handler: apiserver.RecoverPanics(handler),
|
||||
ReadTimeout: 5 * time.Minute,
|
||||
WriteTimeout: 5 * time.Minute,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
|
|
|
@ -206,7 +206,7 @@ func TestNotFound(t *testing.T) {
|
|||
for k, v := range cases {
|
||||
request, err := http.NewRequest(v.Method, server.URL+v.Path, nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
response, err := client.Do(request)
|
||||
|
|
|
@ -24,9 +24,34 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// ReadOnly passes all GET requests on to handler, and returns an error on all other requests.
|
||||
func ReadOnly(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "GET" {
|
||||
handler.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, "This is a read-only endpoint.")
|
||||
})
|
||||
}
|
||||
|
||||
// RateLimit uses rl to rate limit accepting requests to 'handler'.
|
||||
func RateLimit(rl util.RateLimiter, handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if rl.CanAccept() {
|
||||
handler.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
fmt.Fprintf(w, "Rate limit exceeded.")
|
||||
})
|
||||
}
|
||||
|
||||
// RecoverPanics wraps an http Handler to recover and log panics.
|
||||
func RecoverPanics(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fakeRL bool
|
||||
|
||||
func (fakeRL) Stop() {}
|
||||
func (f fakeRL) CanAccept() bool { return bool(f) }
|
||||
|
||||
func TestRateLimit(t *testing.T) {
|
||||
for _, allow := range []bool{true, false} {
|
||||
rl := fakeRL(allow)
|
||||
server := httptest.NewServer(RateLimit(rl, http.HandlerFunc(
|
||||
func(w http.ResponseWriter, req *http.Request) {
|
||||
if !allow {
|
||||
t.Errorf("Unexpected call")
|
||||
}
|
||||
},
|
||||
)))
|
||||
http.Get(server.URL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadOnly(t *testing.T) {
|
||||
server := httptest.NewServer(ReadOnly(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != "GET" {
|
||||
t.Errorf("Unexpected call: %v", req.Method)
|
||||
}
|
||||
},
|
||||
)))
|
||||
for _, verb := range []string{"GET", "POST", "PUT", "DELETE", "CREATE"} {
|
||||
req, err := http.NewRequest(verb, server.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't make request: %v", err)
|
||||
}
|
||||
http.DefaultClient.Do(req)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue