genericapiserver_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package genericapiserver
  14. import (
  15. "crypto/tls"
  16. "encoding/json"
  17. "fmt"
  18. "io/ioutil"
  19. "net"
  20. "net/http"
  21. "net/http/httptest"
  22. "reflect"
  23. "strconv"
  24. "testing"
  25. "k8s.io/kubernetes/pkg/api"
  26. "k8s.io/kubernetes/pkg/api/rest"
  27. "k8s.io/kubernetes/pkg/api/testapi"
  28. "k8s.io/kubernetes/pkg/api/unversioned"
  29. "k8s.io/kubernetes/pkg/apimachinery/registered"
  30. "k8s.io/kubernetes/pkg/apis/extensions"
  31. "k8s.io/kubernetes/pkg/apiserver"
  32. etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
  33. utilnet "k8s.io/kubernetes/pkg/util/net"
  34. "github.com/stretchr/testify/assert"
  35. )
  36. // setUp is a convience function for setting up for (most) tests.
  37. func setUp(t *testing.T) (GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
  38. etcdServer := etcdtesting.NewEtcdTestClientServer(t)
  39. genericapiserver := GenericAPIServer{}
  40. config := Config{}
  41. config.PublicAddress = net.ParseIP("192.168.10.4")
  42. return genericapiserver, etcdServer, config, assert.New(t)
  43. }
  44. func newMaster(t *testing.T) (*GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
  45. _, etcdserver, config, assert := setUp(t)
  46. config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
  47. config.ProxyTLSClientConfig = &tls.Config{}
  48. config.Serializer = api.Codecs
  49. config.APIPrefix = "/api"
  50. config.APIGroupPrefix = "/apis"
  51. s, err := New(&config)
  52. if err != nil {
  53. t.Fatalf("Error in bringing up the server: %v", err)
  54. }
  55. return s, etcdserver, config, assert
  56. }
  57. // TestNew verifies that the New function returns a GenericAPIServer
  58. // using the configuration properly.
  59. func TestNew(t *testing.T) {
  60. s, etcdserver, config, assert := newMaster(t)
  61. defer etcdserver.Terminate(t)
  62. // Verify many of the variables match their config counterparts
  63. assert.Equal(s.enableLogsSupport, config.EnableLogsSupport)
  64. assert.Equal(s.enableUISupport, config.EnableUISupport)
  65. assert.Equal(s.enableSwaggerSupport, config.EnableSwaggerSupport)
  66. assert.Equal(s.enableSwaggerUI, config.EnableSwaggerUI)
  67. assert.Equal(s.enableProfiling, config.EnableProfiling)
  68. assert.Equal(s.APIPrefix, config.APIPrefix)
  69. assert.Equal(s.APIGroupPrefix, config.APIGroupPrefix)
  70. assert.Equal(s.corsAllowedOriginList, config.CorsAllowedOriginList)
  71. assert.Equal(s.authenticator, config.Authenticator)
  72. assert.Equal(s.authorizer, config.Authorizer)
  73. assert.Equal(s.AdmissionControl, config.AdmissionControl)
  74. assert.Equal(s.RequestContextMapper, config.RequestContextMapper)
  75. assert.Equal(s.cacheTimeout, config.CacheTimeout)
  76. assert.Equal(s.ExternalAddress, config.ExternalHost)
  77. assert.Equal(s.ClusterIP, config.PublicAddress)
  78. assert.Equal(s.PublicReadWritePort, config.ReadWritePort)
  79. assert.Equal(s.ServiceReadWriteIP, config.ServiceReadWriteIP)
  80. // These functions should point to the same memory location
  81. serverDialer, _ := utilnet.Dialer(s.ProxyTransport)
  82. serverDialerFunc := fmt.Sprintf("%p", serverDialer)
  83. configDialerFunc := fmt.Sprintf("%p", config.ProxyDialer)
  84. assert.Equal(serverDialerFunc, configDialerFunc)
  85. assert.Equal(s.ProxyTransport.(*http.Transport).TLSClientConfig, config.ProxyTLSClientConfig)
  86. }
  87. // Verifies that AddGroupVersions works as expected.
  88. func TestInstallAPIGroups(t *testing.T) {
  89. _, etcdserver, config, assert := setUp(t)
  90. defer etcdserver.Terminate(t)
  91. config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil }
  92. config.ProxyTLSClientConfig = &tls.Config{}
  93. config.APIPrefix = "/apiPrefix"
  94. config.APIGroupPrefix = "/apiGroupPrefix"
  95. config.Serializer = api.Codecs
  96. s, err := New(&config)
  97. if err != nil {
  98. t.Fatalf("Error in bringing up the server: %v", err)
  99. }
  100. apiGroupMeta := registered.GroupOrDie(api.GroupName)
  101. extensionsGroupMeta := registered.GroupOrDie(extensions.GroupName)
  102. apiGroupsInfo := []APIGroupInfo{
  103. {
  104. // legacy group version
  105. GroupMeta: *apiGroupMeta,
  106. VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
  107. IsLegacyGroup: true,
  108. ParameterCodec: api.ParameterCodec,
  109. NegotiatedSerializer: api.Codecs,
  110. },
  111. {
  112. // extensions group version
  113. GroupMeta: *extensionsGroupMeta,
  114. VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
  115. OptionsExternalVersion: &apiGroupMeta.GroupVersion,
  116. ParameterCodec: api.ParameterCodec,
  117. NegotiatedSerializer: api.Codecs,
  118. },
  119. }
  120. s.InstallAPIGroups(apiGroupsInfo)
  121. server := httptest.NewServer(s.HandlerContainer.ServeMux)
  122. defer server.Close()
  123. validPaths := []string{
  124. // "/api"
  125. config.APIPrefix,
  126. // "/api/v1"
  127. config.APIPrefix + "/" + apiGroupMeta.GroupVersion.Version,
  128. // "/apis/extensions"
  129. config.APIGroupPrefix + "/" + extensionsGroupMeta.GroupVersion.Group,
  130. // "/apis/extensions/v1beta1"
  131. config.APIGroupPrefix + "/" + extensionsGroupMeta.GroupVersion.String(),
  132. }
  133. for _, path := range validPaths {
  134. _, err := http.Get(server.URL + path)
  135. if !assert.NoError(err) {
  136. t.Errorf("unexpected error: %v, for path: %s", err, path)
  137. }
  138. }
  139. }
  140. // TestNewHandlerContainer verifies that NewHandlerContainer uses the
  141. // mux provided
  142. func TestNewHandlerContainer(t *testing.T) {
  143. assert := assert.New(t)
  144. mux := http.NewServeMux()
  145. container := NewHandlerContainer(mux, nil)
  146. assert.Equal(mux, container.ServeMux, "ServerMux's do not match")
  147. }
  148. // TestHandleWithAuth verifies HandleWithAuth adds the path
  149. // to the MuxHelper.RegisteredPaths.
  150. func TestHandleWithAuth(t *testing.T) {
  151. server, etcdserver, _, assert := setUp(t)
  152. defer etcdserver.Terminate(t)
  153. mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
  154. server.MuxHelper = &mh
  155. handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
  156. server.HandleWithAuth("/test", http.HandlerFunc(handler))
  157. assert.Contains(server.MuxHelper.RegisteredPaths, "/test", "Path not found in MuxHelper")
  158. }
  159. // TestHandleFuncWithAuth verifies HandleFuncWithAuth adds the path
  160. // to the MuxHelper.RegisteredPaths.
  161. func TestHandleFuncWithAuth(t *testing.T) {
  162. server, etcdserver, _, assert := setUp(t)
  163. defer etcdserver.Terminate(t)
  164. mh := apiserver.MuxHelper{Mux: http.NewServeMux()}
  165. server.MuxHelper = &mh
  166. handler := func(r http.ResponseWriter, w *http.Request) { w.Write(nil) }
  167. server.HandleFuncWithAuth("/test", handler)
  168. assert.Contains(server.MuxHelper.RegisteredPaths, "/test", "Path not found in MuxHelper")
  169. }
  170. // TestInstallSwaggerAPI verifies that the swagger api is added
  171. // at the proper endpoint.
  172. func TestInstallSwaggerAPI(t *testing.T) {
  173. server, etcdserver, _, assert := setUp(t)
  174. defer etcdserver.Terminate(t)
  175. mux := http.NewServeMux()
  176. server.HandlerContainer = NewHandlerContainer(mux, nil)
  177. // Ensure swagger isn't installed without the call
  178. ws := server.HandlerContainer.RegisteredWebServices()
  179. if !assert.Equal(len(ws), 0) {
  180. for x := range ws {
  181. assert.NotEqual("/swaggerapi", ws[x].RootPath(), "SwaggerAPI was installed without a call to InstallSwaggerAPI()")
  182. }
  183. }
  184. // Install swagger and test
  185. server.InstallSwaggerAPI()
  186. ws = server.HandlerContainer.RegisteredWebServices()
  187. if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
  188. assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
  189. }
  190. // Empty externalHost verification
  191. mux = http.NewServeMux()
  192. server.HandlerContainer = NewHandlerContainer(mux, nil)
  193. server.ExternalAddress = ""
  194. server.ClusterIP = net.IPv4(10, 10, 10, 10)
  195. server.PublicReadWritePort = 1010
  196. server.InstallSwaggerAPI()
  197. if assert.NotEqual(0, len(ws), "SwaggerAPI not installed.") {
  198. assert.Equal("/swaggerapi/", ws[0].RootPath(), "SwaggerAPI did not install to the proper path. %s != /swaggerapi", ws[0].RootPath())
  199. }
  200. }
  201. func decodeResponse(resp *http.Response, obj interface{}) error {
  202. defer resp.Body.Close()
  203. data, err := ioutil.ReadAll(resp.Body)
  204. if err != nil {
  205. return err
  206. }
  207. if err := json.Unmarshal(data, obj); err != nil {
  208. return err
  209. }
  210. return nil
  211. }
  212. func getGroupList(server *httptest.Server) (*unversioned.APIGroupList, error) {
  213. resp, err := http.Get(server.URL + "/apis")
  214. if err != nil {
  215. return nil, err
  216. }
  217. if resp.StatusCode != http.StatusOK {
  218. return nil, fmt.Errorf("unexpected server response, expected %d, actual: %d", http.StatusOK, resp.StatusCode)
  219. }
  220. groupList := unversioned.APIGroupList{}
  221. err = decodeResponse(resp, &groupList)
  222. return &groupList, err
  223. }
  224. func TestDiscoveryAtAPIS(t *testing.T) {
  225. master, etcdserver, _, assert := newMaster(t)
  226. defer etcdserver.Terminate(t)
  227. server := httptest.NewServer(master.HandlerContainer.ServeMux)
  228. groupList, err := getGroupList(server)
  229. if err != nil {
  230. t.Fatalf("unexpected error: %v", err)
  231. }
  232. assert.Equal(0, len(groupList.Groups))
  233. // Add a Group.
  234. extensionsVersions := []unversioned.GroupVersionForDiscovery{
  235. {
  236. GroupVersion: testapi.Extensions.GroupVersion().String(),
  237. Version: testapi.Extensions.GroupVersion().Version,
  238. },
  239. }
  240. extensionsPreferredVersion := unversioned.GroupVersionForDiscovery{
  241. GroupVersion: extensions.GroupName + "/preferred",
  242. Version: "preferred",
  243. }
  244. master.AddAPIGroupForDiscovery(unversioned.APIGroup{
  245. Name: extensions.GroupName,
  246. Versions: extensionsVersions,
  247. PreferredVersion: extensionsPreferredVersion,
  248. })
  249. groupList, err = getGroupList(server)
  250. if err != nil {
  251. t.Fatalf("unexpected error: %v", err)
  252. }
  253. assert.Equal(1, len(groupList.Groups))
  254. groupListGroup := groupList.Groups[0]
  255. assert.Equal(extensions.GroupName, groupListGroup.Name)
  256. assert.Equal(extensionsVersions, groupListGroup.Versions)
  257. assert.Equal(extensionsPreferredVersion, groupListGroup.PreferredVersion)
  258. assert.Equal(master.getServerAddressByClientCIDRs(&http.Request{}), groupListGroup.ServerAddressByClientCIDRs)
  259. // Remove the group.
  260. master.RemoveAPIGroupForDiscovery(extensions.GroupName)
  261. groupList, err = getGroupList(server)
  262. if err != nil {
  263. t.Fatalf("unexpected error: %v", err)
  264. }
  265. assert.Equal(0, len(groupList.Groups))
  266. }
  267. func TestGetServerAddressByClientCIDRs(t *testing.T) {
  268. s, etcdserver, _, _ := newMaster(t)
  269. defer etcdserver.Terminate(t)
  270. publicAddressCIDRMap := []unversioned.ServerAddressByClientCIDR{
  271. {
  272. ClientCIDR: "0.0.0.0/0",
  273. ServerAddress: s.ExternalAddress,
  274. },
  275. }
  276. internalAddressCIDRMap := []unversioned.ServerAddressByClientCIDR{
  277. publicAddressCIDRMap[0],
  278. {
  279. ClientCIDR: s.ServiceClusterIPRange.String(),
  280. ServerAddress: net.JoinHostPort(s.ServiceReadWriteIP.String(), strconv.Itoa(s.ServiceReadWritePort)),
  281. },
  282. }
  283. internalIP := "10.0.0.1"
  284. publicIP := "1.1.1.1"
  285. testCases := []struct {
  286. Request http.Request
  287. ExpectedMap []unversioned.ServerAddressByClientCIDR
  288. }{
  289. {
  290. Request: http.Request{},
  291. ExpectedMap: publicAddressCIDRMap,
  292. },
  293. {
  294. Request: http.Request{
  295. Header: map[string][]string{
  296. "X-Real-Ip": {internalIP},
  297. },
  298. },
  299. ExpectedMap: internalAddressCIDRMap,
  300. },
  301. {
  302. Request: http.Request{
  303. Header: map[string][]string{
  304. "X-Real-Ip": {publicIP},
  305. },
  306. },
  307. ExpectedMap: publicAddressCIDRMap,
  308. },
  309. {
  310. Request: http.Request{
  311. Header: map[string][]string{
  312. "X-Forwarded-For": {internalIP},
  313. },
  314. },
  315. ExpectedMap: internalAddressCIDRMap,
  316. },
  317. {
  318. Request: http.Request{
  319. Header: map[string][]string{
  320. "X-Forwarded-For": {publicIP},
  321. },
  322. },
  323. ExpectedMap: publicAddressCIDRMap,
  324. },
  325. {
  326. Request: http.Request{
  327. RemoteAddr: internalIP,
  328. },
  329. ExpectedMap: internalAddressCIDRMap,
  330. },
  331. {
  332. Request: http.Request{
  333. RemoteAddr: publicIP,
  334. },
  335. ExpectedMap: publicAddressCIDRMap,
  336. },
  337. {
  338. Request: http.Request{
  339. RemoteAddr: "invalidIP",
  340. },
  341. ExpectedMap: publicAddressCIDRMap,
  342. },
  343. }
  344. for i, test := range testCases {
  345. if a, e := s.getServerAddressByClientCIDRs(&test.Request), test.ExpectedMap; reflect.DeepEqual(e, a) != true {
  346. t.Fatalf("test case %d failed. expected: %v, actual: %v", i+1, e, a)
  347. }
  348. }
  349. }