master_utils.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. Copyright 2014 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 framework
  14. import (
  15. "flag"
  16. "net"
  17. "net/http"
  18. "net/http/httptest"
  19. "path"
  20. "strconv"
  21. "time"
  22. "github.com/go-openapi/spec"
  23. "github.com/pborman/uuid"
  24. "k8s.io/apimachinery/pkg/runtime/schema"
  25. "k8s.io/apimachinery/pkg/util/wait"
  26. authauthenticator "k8s.io/apiserver/pkg/authentication/authenticator"
  27. "k8s.io/apiserver/pkg/authentication/authenticatorfactory"
  28. authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
  29. "k8s.io/apiserver/pkg/authentication/user"
  30. "k8s.io/apiserver/pkg/authorization/authorizer"
  31. "k8s.io/apiserver/pkg/authorization/authorizerfactory"
  32. authorizerunion "k8s.io/apiserver/pkg/authorization/union"
  33. openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
  34. genericapiserver "k8s.io/apiserver/pkg/server"
  35. "k8s.io/apiserver/pkg/server/options"
  36. serverstorage "k8s.io/apiserver/pkg/server/storage"
  37. "k8s.io/apiserver/pkg/storage/storagebackend"
  38. "k8s.io/client-go/informers"
  39. clientset "k8s.io/client-go/kubernetes"
  40. restclient "k8s.io/client-go/rest"
  41. "k8s.io/klog"
  42. openapicommon "k8s.io/kube-openapi/pkg/common"
  43. "k8s.io/kubernetes/pkg/api/legacyscheme"
  44. "k8s.io/kubernetes/pkg/generated/openapi"
  45. "k8s.io/kubernetes/pkg/kubeapiserver"
  46. kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
  47. "k8s.io/kubernetes/pkg/master"
  48. "k8s.io/kubernetes/pkg/version"
  49. )
  50. // Config is a struct of configuration directives for NewMasterComponents.
  51. type Config struct {
  52. // If nil, a default is used, partially filled configs will not get populated.
  53. MasterConfig *master.Config
  54. StartReplicationManager bool
  55. // Client throttling qps
  56. QPS float32
  57. // Client burst qps, also burst replicas allowed in rc manager
  58. Burst int
  59. // TODO: Add configs for endpoints controller, scheduler etc
  60. }
  61. // alwaysAllow always allows an action
  62. type alwaysAllow struct{}
  63. func (alwaysAllow) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
  64. return authorizer.DecisionAllow, "always allow", nil
  65. }
  66. // alwaysEmpty simulates "no authentication" for old tests
  67. func alwaysEmpty(req *http.Request) (*authauthenticator.Response, bool, error) {
  68. return &authauthenticator.Response{
  69. User: &user.DefaultInfo{
  70. Name: "",
  71. },
  72. }, true, nil
  73. }
  74. // MasterReceiver can be used to provide the master to a custom incoming server function
  75. type MasterReceiver interface {
  76. SetMaster(m *master.Master)
  77. }
  78. // MasterHolder implements
  79. type MasterHolder struct {
  80. Initialized chan struct{}
  81. M *master.Master
  82. }
  83. // SetMaster assigns the current master.
  84. func (h *MasterHolder) SetMaster(m *master.Master) {
  85. h.M = m
  86. close(h.Initialized)
  87. }
  88. // DefaultOpenAPIConfig returns an openapicommon.Config initialized to default values.
  89. func DefaultOpenAPIConfig() *openapicommon.Config {
  90. openAPIConfig := genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(legacyscheme.Scheme))
  91. openAPIConfig.Info = &spec.Info{
  92. InfoProps: spec.InfoProps{
  93. Title: "Kubernetes",
  94. Version: "unversioned",
  95. },
  96. }
  97. openAPIConfig.DefaultResponse = &spec.Response{
  98. ResponseProps: spec.ResponseProps{
  99. Description: "Default Response.",
  100. },
  101. }
  102. openAPIConfig.GetDefinitions = openapi.GetOpenAPIDefinitions
  103. return openAPIConfig
  104. }
  105. // startMasterOrDie starts a kubernetes master and an httpserver to handle api requests
  106. func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Server, masterReceiver MasterReceiver) (*master.Master, *httptest.Server, CloseFunc) {
  107. var m *master.Master
  108. var s *httptest.Server
  109. // Ensure we log at least level 4
  110. v := flag.Lookup("v").Value
  111. level, _ := strconv.Atoi(v.String())
  112. if level < 4 {
  113. v.Set("4")
  114. }
  115. if incomingServer != nil {
  116. s = incomingServer
  117. } else {
  118. s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  119. m.GenericAPIServer.Handler.ServeHTTP(w, req)
  120. }))
  121. }
  122. stopCh := make(chan struct{})
  123. closeFn := func() {
  124. m.GenericAPIServer.RunPreShutdownHooks()
  125. close(stopCh)
  126. s.Close()
  127. }
  128. if masterConfig == nil {
  129. masterConfig = NewMasterConfig()
  130. masterConfig.GenericConfig.OpenAPIConfig = DefaultOpenAPIConfig()
  131. }
  132. // set the loopback client config
  133. if masterConfig.GenericConfig.LoopbackClientConfig == nil {
  134. masterConfig.GenericConfig.LoopbackClientConfig = &restclient.Config{QPS: 50, Burst: 100, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
  135. }
  136. masterConfig.GenericConfig.LoopbackClientConfig.Host = s.URL
  137. privilegedLoopbackToken := uuid.NewRandom().String()
  138. // wrap any available authorizer
  139. tokens := make(map[string]*user.DefaultInfo)
  140. tokens[privilegedLoopbackToken] = &user.DefaultInfo{
  141. Name: user.APIServerUser,
  142. UID: uuid.NewRandom().String(),
  143. Groups: []string{user.SystemPrivilegedGroup},
  144. }
  145. tokenAuthenticator := authenticatorfactory.NewFromTokens(tokens)
  146. if masterConfig.GenericConfig.Authentication.Authenticator == nil {
  147. masterConfig.GenericConfig.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, authauthenticator.RequestFunc(alwaysEmpty))
  148. } else {
  149. masterConfig.GenericConfig.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, masterConfig.GenericConfig.Authentication.Authenticator)
  150. }
  151. if masterConfig.GenericConfig.Authorization.Authorizer != nil {
  152. tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
  153. masterConfig.GenericConfig.Authorization.Authorizer = authorizerunion.New(tokenAuthorizer, masterConfig.GenericConfig.Authorization.Authorizer)
  154. } else {
  155. masterConfig.GenericConfig.Authorization.Authorizer = alwaysAllow{}
  156. }
  157. masterConfig.GenericConfig.LoopbackClientConfig.BearerToken = privilegedLoopbackToken
  158. clientset, err := clientset.NewForConfig(masterConfig.GenericConfig.LoopbackClientConfig)
  159. if err != nil {
  160. klog.Fatal(err)
  161. }
  162. masterConfig.ExtraConfig.VersionedInformers = informers.NewSharedInformerFactory(clientset, masterConfig.GenericConfig.LoopbackClientConfig.Timeout)
  163. m, err = masterConfig.Complete().New(genericapiserver.NewEmptyDelegate())
  164. if err != nil {
  165. closeFn()
  166. klog.Fatalf("error in bringing up the master: %v", err)
  167. }
  168. if masterReceiver != nil {
  169. masterReceiver.SetMaster(m)
  170. }
  171. // TODO have this start method actually use the normal start sequence for the API server
  172. // this method never actually calls the `Run` method for the API server
  173. // fire the post hooks ourselves
  174. m.GenericAPIServer.PrepareRun()
  175. m.GenericAPIServer.RunPostStartHooks(stopCh)
  176. cfg := *masterConfig.GenericConfig.LoopbackClientConfig
  177. cfg.ContentConfig.GroupVersion = &schema.GroupVersion{}
  178. privilegedClient, err := restclient.RESTClientFor(&cfg)
  179. if err != nil {
  180. closeFn()
  181. klog.Fatal(err)
  182. }
  183. var lastHealthContent []byte
  184. err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) {
  185. result := privilegedClient.Get().AbsPath("/healthz").Do()
  186. status := 0
  187. result.StatusCode(&status)
  188. if status == 200 {
  189. return true, nil
  190. }
  191. lastHealthContent, _ = result.Raw()
  192. return false, nil
  193. })
  194. if err != nil {
  195. closeFn()
  196. klog.Errorf("last health content: %q", string(lastHealthContent))
  197. klog.Fatal(err)
  198. }
  199. return m, s, closeFn
  200. }
  201. // NewIntegrationTestMasterConfig returns the master config appropriate for most integration tests.
  202. func NewIntegrationTestMasterConfig() *master.Config {
  203. return NewIntegrationTestMasterConfigWithOptions(&MasterConfigOptions{})
  204. }
  205. // NewIntegrationTestMasterConfigWithOptions returns the master config appropriate for most integration tests
  206. // configured with the provided options.
  207. func NewIntegrationTestMasterConfigWithOptions(opts *MasterConfigOptions) *master.Config {
  208. masterConfig := NewMasterConfigWithOptions(opts)
  209. masterConfig.GenericConfig.PublicAddress = net.ParseIP("192.168.10.4")
  210. masterConfig.ExtraConfig.APIResourceConfigSource = master.DefaultAPIResourceConfigSource()
  211. // TODO: get rid of these tests or port them to secure serving
  212. masterConfig.GenericConfig.SecureServing = &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}}
  213. return masterConfig
  214. }
  215. // MasterConfigOptions are the configurable options for a new integration test master config.
  216. type MasterConfigOptions struct {
  217. EtcdOptions *options.EtcdOptions
  218. }
  219. // DefaultEtcdOptions are the default EtcdOptions for use with integration tests.
  220. func DefaultEtcdOptions() *options.EtcdOptions {
  221. // This causes the integration tests to exercise the etcd
  222. // prefix code, so please don't change without ensuring
  223. // sufficient coverage in other ways.
  224. etcdOptions := options.NewEtcdOptions(storagebackend.NewDefaultConfig(uuid.New(), nil))
  225. etcdOptions.StorageConfig.Transport.ServerList = []string{GetEtcdURL()}
  226. return etcdOptions
  227. }
  228. // NewMasterConfig returns a basic master config.
  229. func NewMasterConfig() *master.Config {
  230. return NewMasterConfigWithOptions(&MasterConfigOptions{})
  231. }
  232. // NewMasterConfigWithOptions returns a basic master config configured with the provided options.
  233. func NewMasterConfigWithOptions(opts *MasterConfigOptions) *master.Config {
  234. etcdOptions := DefaultEtcdOptions()
  235. if opts.EtcdOptions != nil {
  236. etcdOptions = opts.EtcdOptions
  237. }
  238. storageConfig := kubeapiserver.NewStorageFactoryConfig()
  239. storageConfig.ApiResourceConfig = serverstorage.NewResourceConfig()
  240. completedStorageConfig, err := storageConfig.Complete(etcdOptions)
  241. if err != nil {
  242. panic(err)
  243. }
  244. storageFactory, err := completedStorageConfig.New()
  245. if err != nil {
  246. panic(err)
  247. }
  248. genericConfig := genericapiserver.NewConfig(legacyscheme.Codecs)
  249. kubeVersion := version.Get()
  250. genericConfig.Version = &kubeVersion
  251. genericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
  252. // TODO: get rid of these tests or port them to secure serving
  253. genericConfig.SecureServing = &genericapiserver.SecureServingInfo{Listener: fakeLocalhost443Listener{}}
  254. err = etcdOptions.ApplyWithStorageFactoryTo(storageFactory, genericConfig)
  255. if err != nil {
  256. panic(err)
  257. }
  258. return &master.Config{
  259. GenericConfig: genericConfig,
  260. ExtraConfig: master.ExtraConfig{
  261. APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
  262. StorageFactory: storageFactory,
  263. KubeletClientConfig: kubeletclient.KubeletClientConfig{Port: 10250},
  264. APIServerServicePort: 443,
  265. MasterCount: 1,
  266. },
  267. }
  268. }
  269. // CloseFunc can be called to cleanup the master
  270. type CloseFunc func()
  271. // RunAMaster starts a master with the provided config.
  272. func RunAMaster(masterConfig *master.Config) (*master.Master, *httptest.Server, CloseFunc) {
  273. if masterConfig == nil {
  274. masterConfig = NewMasterConfig()
  275. masterConfig.GenericConfig.EnableProfiling = true
  276. }
  277. return startMasterOrDie(masterConfig, nil, nil)
  278. }
  279. // RunAMasterUsingServer starts up a master using the provided config on the specified server.
  280. func RunAMasterUsingServer(masterConfig *master.Config, s *httptest.Server, masterReceiver MasterReceiver) (*master.Master, *httptest.Server, CloseFunc) {
  281. return startMasterOrDie(masterConfig, s, masterReceiver)
  282. }
  283. // SharedEtcd creates a storage config for a shared etcd instance, with a unique prefix.
  284. func SharedEtcd() *storagebackend.Config {
  285. cfg := storagebackend.NewDefaultConfig(path.Join(uuid.New(), "registry"), nil)
  286. cfg.Transport.ServerList = []string{GetEtcdURL()}
  287. return cfg
  288. }
  289. type fakeLocalhost443Listener struct{}
  290. func (fakeLocalhost443Listener) Accept() (net.Conn, error) {
  291. return nil, nil
  292. }
  293. func (fakeLocalhost443Listener) Close() error {
  294. return nil
  295. }
  296. func (fakeLocalhost443Listener) Addr() net.Addr {
  297. return &net.TCPAddr{
  298. IP: net.IPv4(127, 0, 0, 1),
  299. Port: 443,
  300. }
  301. }