diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index c875b055f22..fbafa85a7cf 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -171,7 +171,10 @@ func startComponents(firstManifestURL, secondManifestURL string) (string, string masterConfig.CacheTimeout = 2 * time.Second // Create a master and install handlers into mux. - m := master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + glog.Fatalf("Error in bringing up the master: %v", err) + } handler.delegate = m.Handler // Scheduler diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 7f8adca08a6..ff3e00d6c1c 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -401,7 +401,10 @@ func Run(s *options.APIServer) error { Tunneler: tunneler, } - m := master.New(config) + m, err := master.New(config) + if err != nil { + return err + } m.Run(s.ServerRunOptions) return nil } diff --git a/examples/apiserver/README.md b/examples/apiserver/README.md index 522544984f8..d493f7fbdea 100644 --- a/examples/apiserver/README.md +++ b/examples/apiserver/README.md @@ -39,6 +39,12 @@ API objects. Some relevant issues: This code here is to examplify what it takes to write your own API server. +To start this example api server, run: + +``` +$ go run examples/apiserver/server/main.go +``` + [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/apiserver/README.md?pixel)]() diff --git a/examples/apiserver/server.go b/examples/apiserver/apiserver.go similarity index 84% rename from examples/apiserver/server.go rename to examples/apiserver/apiserver.go index cd565e43a3c..e515f349b55 100644 --- a/examples/apiserver/server.go +++ b/examples/apiserver/apiserver.go @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package apiserver import ( - "github.com/golang/glog" + "fmt" + "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1" testgroupetcd "k8s.io/kubernetes/examples/apiserver/rest" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apimachinery" "k8s.io/kubernetes/pkg/apimachinery/registered" @@ -44,23 +46,27 @@ func newStorageDestinations(groupName string, groupMeta *apimachinery.GroupMeta) return &storageDestinations, nil } -func main() { +func Run() error { config := genericapiserver.Config{ EnableIndex: true, APIPrefix: "/api", APIGroupPrefix: "/apis", + Serializer: api.Codecs, + } + s, err := genericapiserver.New(&config) + if err != nil { + return fmt.Errorf("Error in bringing up the server: %v", err) } - s := genericapiserver.New(&config) groupVersion := v1.SchemeGroupVersion groupName := groupVersion.Group groupMeta, err := registered.Group(groupName) if err != nil { - glog.Fatalf("%v", err) + return fmt.Errorf("%v", err) } storageDestinations, err := newStorageDestinations(groupName, groupMeta) if err != nil { - glog.Fatalf("Unable to init etcd: %v", err) + return fmt.Errorf("Unable to init etcd: %v", err) } restStorageMap := map[string]rest.Storage{ "testtypes": testgroupetcd.NewREST(storageDestinations.Get(groupName, "testtype"), s.StorageDecorator()), @@ -70,9 +76,12 @@ func main() { VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ groupVersion.Version: restStorageMap, }, + Scheme: api.Scheme, + NegotiatedSerializer: api.Codecs, } if err := s.InstallAPIGroups([]genericapiserver.APIGroupInfo{apiGroupInfo}); err != nil { - glog.Fatalf("Error in installing API: %v", err) + return fmt.Errorf("Error in installing API: %v", err) } s.Run(genericapiserver.NewServerRunOptions()) + return nil } diff --git a/examples/apiserver/apiserver_test.go b/examples/apiserver/apiserver_test.go new file mode 100644 index 00000000000..6f74dedd397 --- /dev/null +++ b/examples/apiserver/apiserver_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2016 The Kubernetes Authors 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 ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1" + + "github.com/stretchr/testify/assert" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +var serverIP = "http://localhost:8080" + +var groupVersion = v1.SchemeGroupVersion + +func TestRun(t *testing.T) { + go func() { + if err := Run(); err != nil { + t.Fatalf("Error in bringing up the server: %v", err) + } + }() + if err := waitForApiserverUp(); err != nil { + t.Fatalf("%v", err) + } + testAPIGroup(t) + testAPIResourceList(t) +} + +func waitForApiserverUp() error { + for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) { + _, err := http.Get(serverIP) + if err == nil { + return nil + } + } + return fmt.Errorf("waiting for apiserver timed out") +} + +func readResponse(serverURL string) ([]byte, error) { + response, err := http.Get(serverURL) + if err != nil { + return nil, fmt.Errorf("Error in fetching %s: %v", serverURL, err) + } + defer response.Body.Close() + contents, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("Error reading response from %s: %v", serverURL, err) + } + return contents, nil +} + +func testAPIGroup(t *testing.T) { + serverURL := serverIP + "/apis/testgroup" + contents, err := readResponse(serverURL) + if err != nil { + t.Fatalf("%v", err) + } + var apiGroup unversioned.APIGroup + err = json.Unmarshal(contents, &apiGroup) + if err != nil { + t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) + } + assert.Equal(t, apiGroup.APIVersion, groupVersion.Version) + assert.Equal(t, apiGroup.Name, groupVersion.Group) + assert.Equal(t, 1, len(apiGroup.Versions)) + assert.Equal(t, apiGroup.Versions[0].GroupVersion, groupVersion.String()) + assert.Equal(t, apiGroup.Versions[0].Version, groupVersion.Version) + assert.Equal(t, apiGroup.Versions[0], apiGroup.PreferredVersion) +} + +func testAPIResourceList(t *testing.T) { + serverURL := serverIP + "/apis/testgroup/v1" + contents, err := readResponse(serverURL) + if err != nil { + t.Fatalf("%v", err) + } + var apiResourceList unversioned.APIResourceList + err = json.Unmarshal(contents, &apiResourceList) + if err != nil { + t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) + } + assert.Equal(t, apiResourceList.APIVersion, groupVersion.Version) + assert.Equal(t, apiResourceList.GroupVersion, groupVersion.String()) + assert.Equal(t, 1, len(apiResourceList.APIResources)) + assert.Equal(t, apiResourceList.APIResources[0].Name, "testtypes") + assert.True(t, apiResourceList.APIResources[0].Namespaced) +} diff --git a/examples/apiserver/server/main.go b/examples/apiserver/server/main.go new file mode 100644 index 00000000000..aec0ef0ec90 --- /dev/null +++ b/examples/apiserver/server/main.go @@ -0,0 +1,29 @@ +/* +Copyright 2016 The Kubernetes Authors 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 main + +import ( + "k8s.io/kubernetes/examples/apiserver" + + "github.com/golang/glog" +) + +func main() { + if err := apiserver.Run(); err != nil { + glog.Fatalf("Error in bringing up the server: %v", err) + } +} diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index ac1f125aa57..881c72e2b58 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -379,7 +379,10 @@ func setDefaults(c *Config) { // If the caller wants to add additional endpoints not using the GenericAPIServer's // auth, then the caller should create a handler for those endpoints, which delegates the // any unhandled paths to "Handler". -func New(c *Config) *GenericAPIServer { +func New(c *Config) (*GenericAPIServer, error) { + if c.Serializer == nil { + return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil") + } setDefaults(c) s := &GenericAPIServer{ @@ -433,7 +436,7 @@ func New(c *Config) *GenericAPIServer { s.init(c) - return s + return s, nil } func (s *GenericAPIServer) NewRequestInfoResolver() *apiserver.RequestInfoResolver { diff --git a/pkg/genericapiserver/genericapiserver_test.go b/pkg/genericapiserver/genericapiserver_test.go index be22c99e342..6b7209a8218 100644 --- a/pkg/genericapiserver/genericapiserver_test.go +++ b/pkg/genericapiserver/genericapiserver_test.go @@ -54,8 +54,12 @@ func TestNew(t *testing.T) { config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil } config.ProxyTLSClientConfig = &tls.Config{} + config.Serializer = api.Codecs - s := New(&config) + s, err := New(&config) + if err != nil { + t.Fatalf("Error in bringing up the server: %v", err) + } // Verify many of the variables match their config counterparts assert.Equal(s.enableLogsSupport, config.EnableLogsSupport) @@ -96,7 +100,11 @@ func TestInstallAPIGroups(t *testing.T) { config.APIGroupPrefix = "/apiGroupPrefix" config.Serializer = api.Codecs - s := New(&config) + s, err := New(&config) + if err != nil { + t.Fatalf("Error in bringing up the server: %v", err) + } + apiGroupMeta := registered.GroupOrDie(api.GroupName) extensionsGroupMeta := registered.GroupOrDie(extensions.GroupName) apiGroupsInfo := []APIGroupInfo{ diff --git a/pkg/master/master.go b/pkg/master/master.go index 30dd63fce4b..1beca735b6f 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -134,12 +134,15 @@ type thirdPartyEntry struct { // Certain config fields will be set to a default value if unset. // Certain config fields must be specified, including: // KubeletClient -func New(c *Config) *Master { +func New(c *Config) (*Master, error) { if c.KubeletClient == nil { - glog.Fatalf("Master.New() called with config.KubeletClient == nil") + return nil, fmt.Errorf("Master.New() called with config.KubeletClient == nil") } - s := genericapiserver.New(c.Config) + s, err := genericapiserver.New(c.Config) + if err != nil { + return nil, err + } m := &Master{ GenericAPIServer: s, @@ -158,7 +161,7 @@ func New(c *Config) *Master { m.NewBootstrapController().Start() } - return m + return m, nil } func resetMetrics(w http.ResponseWriter, req *http.Request) { diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index adb5fe1c971..f480d586b06 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -89,7 +89,11 @@ func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *ass config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil } config.ProxyTLSClientConfig = &tls.Config{} - master := New(&config) + master, err := New(&config) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } + return master, etcdserver, config, assert } diff --git a/test/component/scheduler/perf/util.go b/test/component/scheduler/perf/util.go index 7c5a1d5c190..49b6acaf656 100644 --- a/test/component/scheduler/perf/util.go +++ b/test/component/scheduler/perf/util.go @@ -45,7 +45,10 @@ func mustSetupScheduler() (schedulerConfigFactory *factory.ConfigFactory, destro var m *master.Master masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + panic("error in brining up the master: " + err.Error()) + } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index 3c5335650e5..ba7e1525838 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -394,7 +394,10 @@ func TestAuthModeAlwaysAllow(t *testing.T) { // defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport previousResourceVersion := make(map[string]float64) @@ -498,7 +501,10 @@ func TestAuthModeAlwaysDeny(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authorizer = apiserver.NewAlwaysDenyAuthorizer() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport @@ -555,7 +561,10 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = allowAliceAuthorizer{} masterConfig.AdmissionControl = admit.NewAlwaysAdmit() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport @@ -630,7 +639,10 @@ func TestBobIsForbidden(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = allowAliceAuthorizer{} - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport @@ -679,7 +691,10 @@ func TestUnknownUserIsUnauthorized(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = allowAliceAuthorizer{} - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport @@ -753,7 +768,10 @@ func TestAuthorizationAttributeDetermination(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = trackingAuthorizer - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport @@ -822,7 +840,10 @@ func TestNamespaceAuthorization(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = a - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport @@ -925,7 +946,10 @@ func TestKindAuthorization(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = a - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport @@ -1016,7 +1040,10 @@ func TestReadOnlyAuthorization(t *testing.T) { masterConfig.Authenticator = getTestTokenAuth() masterConfig.Authorizer = a - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } transport := http.DefaultTransport diff --git a/test/integration/extender_test.go b/test/integration/extender_test.go index 4d46221a089..5a1b7a6aee2 100644 --- a/test/integration/extender_test.go +++ b/test/integration/extender_test.go @@ -195,7 +195,10 @@ func TestSchedulerExtender(t *testing.T) { // defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("error in bringing up the master: %v", err) + } restClient := client.NewOrDie(&client.Config{Host: s.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 7128d9e08a1..825801dc961 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -135,7 +135,11 @@ func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Se masterConfig.EnableProfiling = true masterConfig.EnableSwaggerSupport = true } - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + glog.Fatalf("error in bringing up the master: %v", err) + } + return m, s } @@ -289,7 +293,11 @@ func StartPods(numPods int, host string, restClient *client.Client) error { func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) { masterConfig := NewMasterConfig() masterConfig.EnableProfiling = true - m := master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + // TODO: Return error. + glog.Fatalf("error in bringing up the master: %v", err) + } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) diff --git a/test/integration/scheduler_test.go b/test/integration/scheduler_test.go index 21cf958f775..e7c73840d26 100644 --- a/test/integration/scheduler_test.go +++ b/test/integration/scheduler_test.go @@ -62,7 +62,10 @@ func TestUnschedulableNodes(t *testing.T) { // defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } restClient := client.NewOrDie(&client.Config{Host: s.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) @@ -286,7 +289,11 @@ func TestMultiScheduler(t *testing.T) { // defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } + /* This integration tests the multi-scheduler feature in the following way: 1. create a default scheduler @@ -461,7 +468,10 @@ func TestAllocatable(t *testing.T) { defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } // 1. create and start default-scheduler restClient := client.NewOrDie(&client.Config{Host: s.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) diff --git a/test/integration/secret_test.go b/test/integration/secret_test.go index a1e5e7b2c0f..8c89d868465 100644 --- a/test/integration/secret_test.go +++ b/test/integration/secret_test.go @@ -53,7 +53,10 @@ func TestSecrets(t *testing.T) { // defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } framework.DeleteAllEtcdKeys() client := client.NewOrDie(&client.Config{Host: s.URL, ContentConfig: client.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) diff --git a/test/integration/service_account_test.go b/test/integration/service_account_test.go index a00f8af2b66..9fbc74d1f01 100644 --- a/test/integration/service_account_test.go +++ b/test/integration/service_account_test.go @@ -409,7 +409,10 @@ func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, client.C masterConfig.AdmissionControl = serviceAccountAdmission // Create a master and install handlers into mux. - m = master.New(masterConfig) + m, err := master.New(masterConfig) + if err != nil { + t.Fatalf("Error in bringing up the master: %v", err) + } // Start the service account and service account token controllers tokenController := serviceaccountcontroller.NewTokensController(rootClientset, serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)})