Allocate mux in master.New()

Callsites no longer allocate a mux.
Master now exposes method to install handlers
which use the master's auth code.  Not used
but forks (openshift) are expected to use these
methods.  These methods will later be a point
for additional plug-in functionality.
Integration tests now use the master-provided
handler which has auth, rather than using the mux,
which didn't.  Fix TestWhoAmI now that /_whoami
sits behind auth.
pull/6/head
Eric Tune 2014-10-28 13:02:19 -07:00
parent ecdf65f4b1
commit 9713b58caa
5 changed files with 65 additions and 28 deletions

View File

@ -197,7 +197,6 @@ func main() {
} }
n := net.IPNet(portalNet) n := net.IPNet(portalNet)
mux := http.NewServeMux()
config := &master.Config{ config := &master.Config{
Client: client, Client: client,
Cloud: cloud, Cloud: cloud,
@ -215,7 +214,6 @@ func main() {
}, },
}, },
PortalNet: &n, PortalNet: &n,
Mux: mux,
EnableLogsSupport: *enableLogsSupport, EnableLogsSupport: *enableLogsSupport,
EnableUISupport: true, EnableUISupport: true,
APIPrefix: *apiPrefix, APIPrefix: *apiPrefix,

View File

@ -137,14 +137,12 @@ func startComponents(manifestURL string) (apiServerURL string) {
if err != nil { if err != nil {
glog.Fatalf("Nonnumeric port? %v", err) glog.Fatalf("Nonnumeric port? %v", err)
} }
mux := http.NewServeMux()
// Create a master and install handlers into mux. // Create a master and install handlers into mux.
master.New(&master.Config{ m := master.New(&master.Config{
Client: cl, Client: cl,
EtcdHelper: helper, EtcdHelper: helper,
Minions: machineList, Minions: machineList,
KubeletClient: fakeKubeletClient{}, KubeletClient: fakeKubeletClient{},
Mux: mux,
EnableLogsSupport: false, EnableLogsSupport: false,
APIPrefix: "/api", APIPrefix: "/api",
@ -152,7 +150,7 @@ func startComponents(manifestURL string) (apiServerURL string) {
ReadOnlyPort: portNumber, ReadOnlyPort: portNumber,
PublicAddress: host, PublicAddress: host,
}) })
handler.delegate = mux handler.delegate = m.Handler
// Scheduler // Scheduler
schedulerConfigFactory := &factory.ConfigFactory{cl} schedulerConfigFactory := &factory.ConfigFactory{cl}

View File

@ -180,7 +180,28 @@ func setDefaults(c *Config) {
} }
} }
// New returns a new instance of Master connected to the given etcd server. // New returns a new instance of Master from the given config.
// Certain config fields will be set to a default value if unset,
// including:
// PortalNet
// MasterCount
// ReadOnlyPort
// ReadWritePort
// PublicAddress
// Certain config fields must be specified, including:
// KubeletClient
// Public fields:
// Handler -- The returned master has a field TopHandler which is an
// http.Handler which handles all the endpoints provided by the master,
// including the API, the UI, and miscelaneous debugging endpoints. All
// these are subject to authorization and authentication.
// Public methods:
// HandleWithAuth -- Allows caller to add an http.Handler for an endpoint
// that uses the same authentication and authorization (if any is configured)
// as the master's built-in endpoints.
// If the caller wants to add additional endpoints not using the master's
// auth, then the caller should create a handler for those endpoints, which delegates the
// any unhandled paths to "Handler".
func New(c *Config) *Master { func New(c *Config) *Master {
setDefaults(c) setDefaults(c)
minionRegistry := makeMinionRegistry(c) minionRegistry := makeMinionRegistry(c)
@ -198,7 +219,7 @@ func New(c *Config) *Master {
minionRegistry: minionRegistry, minionRegistry: minionRegistry,
client: c.Client, client: c.Client,
portalNet: c.PortalNet, portalNet: c.PortalNet,
mux: c.Mux, mux: http.NewServeMux(),
enableLogsSupport: c.EnableLogsSupport, enableLogsSupport: c.EnableLogsSupport,
enableUISupport: c.EnableUISupport, enableUISupport: c.EnableUISupport,
apiPrefix: c.APIPrefix, apiPrefix: c.APIPrefix,
@ -213,6 +234,24 @@ func New(c *Config) *Master {
return m return m
} }
// HandleWithAuth adds an http.Handler for pattern to an http.ServeMux
// Applies the same authentication and authorization (if any is configured)
// to the request is used for the master's built-in endpoints.
func (m *Master) HandleWithAuth(pattern string, handler http.Handler) {
// TODO: Add a way for plugged-in endpoints to translate their
// URLs into attributes that an Authorizer can understand, and have
// sensible policy defaults for plugged-in endpoints. This will be different
// for generic endpoints versus REST object endpoints.
m.mux.Handle(pattern, handler)
}
// HandleFuncWithAuth adds an http.Handler for pattern to an http.ServeMux
// Applies the same authentication and authorization (if any is configured)
// to the request is used for the master's built-in endpoints.
func (m *Master) HandleFuncWithAuth(pattern string, handler func(http.ResponseWriter, *http.Request)) {
m.mux.HandleFunc(pattern, handler)
}
func makeMinionRegistry(c *Config) minion.Registry { func makeMinionRegistry(c *Config) minion.Registry {
var minionRegistry minion.Registry = etcd.NewRegistry(c.EtcdHelper, nil) var minionRegistry minion.Registry = etcd.NewRegistry(c.EtcdHelper, nil)
if c.HealthCheckMinions { if c.HealthCheckMinions {

View File

@ -63,18 +63,16 @@ xyz987,bob,2
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
mux := http.NewServeMux()
master.New(&master.Config{ m := master.New(&master.Config{
EtcdHelper: helper, EtcdHelper: helper,
Mux: mux,
EnableLogsSupport: false, EnableLogsSupport: false,
EnableUISupport: false, EnableUISupport: false,
APIPrefix: "/api", APIPrefix: "/api",
TokenAuthFile: f.Name(), TokenAuthFile: f.Name(),
}) })
s := httptest.NewServer(mux) s := httptest.NewServer(m.Handler)
defer s.Close() defer s.Close()
// TODO: also test TLS, using e.g NewUnsafeTLSTransport() and NewClientCertTLSTransport() (see pkg/client/helper.go) // TODO: also test TLS, using e.g NewUnsafeTLSTransport() and NewClientCertTLSTransport() (see pkg/client/helper.go)
@ -84,10 +82,11 @@ xyz987,bob,2
name string name string
token string token string
expected string expected string
succeeds bool
}{ }{
{"Valid token", "abc123", "AUTHENTICATED AS alice"}, {"Valid token", "abc123", "AUTHENTICATED AS alice", true},
{"Unknown token", "456jkl", "NOT AUTHENTICATED"}, {"Unknown token", "456jkl", "", false},
{"Empty token", "", "NOT AUTHENTICATED"}, {"No token", "", "", false},
} }
for _, tc := range testCases { for _, tc := range testCases {
req, err := http.NewRequest("GET", s.URL+"/_whoami", nil) req, err := http.NewRequest("GET", s.URL+"/_whoami", nil)
@ -101,14 +100,21 @@ xyz987,bob,2
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
body, err := ioutil.ReadAll(resp.Body) if tc.succeeds {
if err != nil { body, err := ioutil.ReadAll(resp.Body)
t.Fatalf("unexpected error: %v", err) if err != nil {
} t.Fatalf("unexpected error: %v", err)
}
actual := string(body)
if tc.expected != actual {
t.Errorf("case: %s expected: %v got: %v", tc.name, tc.expected, actual)
}
} else {
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("case: %s expected Unauthorized, got: %v", tc.name, resp.StatusCode)
}
actual := string(body)
if tc.expected != actual {
t.Errorf("case: %s expected: %v got: %v", tc.name, tc.expected, actual)
} }
} }
} }

View File

@ -19,7 +19,6 @@ limitations under the License.
package integration package integration
import ( import (
"net/http"
"net/http/httptest" "net/http/httptest"
"reflect" "reflect"
"testing" "testing"
@ -40,17 +39,14 @@ func TestClient(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
mux := http.NewServeMux() m := master.New(&master.Config{
master.New(&master.Config{
EtcdHelper: helper, EtcdHelper: helper,
Mux: mux,
EnableLogsSupport: false, EnableLogsSupport: false,
EnableUISupport: false, EnableUISupport: false,
APIPrefix: "/api", APIPrefix: "/api",
}) })
s := httptest.NewServer(mux) s := httptest.NewServer(m.Handler)
testCases := []string{ testCases := []string{
"v1beta1", "v1beta1",