// TestSecrets tests apiserver-side behavior of creation of secret objects and their use by pods. func TestSecrets(t *testing.T) { helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), }) framework.DeleteAllEtcdKeys() client := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Version()}) DoTestSecrets(t, client, testapi.Version()) }
// startMasterOrDie starts a qingyuan master and an httpserver to handle api requests func startMasterOrDie(masterConfig *master.Config) (*master.Master, *httptest.Server, *tools.EtcdHelper) { var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) var helper tools.EtcdHelper var err error if masterConfig == nil { helper, err = master.NewEtcdHelper(NewEtcdClient(), "", etcdtest.PathPrefix()) if err != nil { glog.Fatalf("Failed to create etcd helper for master %v", err) } masterConfig = &master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableLogsSupport: false, EnableProfiling: true, EnableUISupport: false, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), } } else { helper = masterConfig.EtcdHelper } m = master.New(masterConfig) return m, s, &helper }
// TestUnknownUserIsUnauthorized tests that a user who is unknown // to the authentication system get status code "Unauthorized". // An authorization module is installed in this scenario for integration // test purposes, but requests aren't expected to reach it. func TestUnknownUserIsUnauthorized(t *testing.T) { framework.DeleteAllEtcdKeys() // This file has alice and bob in it. // Set up a master helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authenticator: getTestTokenAuth(), Authorizer: allowAliceAuthorizer{}, AdmissionControl: admit.NewAlwaysAdmit(), }) transport := http.DefaultTransport for _, r := range getTestRequests() { token := UnknownToken bodyBytes := bytes.NewReader([]byte(r.body)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) func() { resp, err := transport.RoundTrip(req) defer resp.Body.Close() if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } // Expect all of unauthenticated user's request to be "Unauthorized" if resp.StatusCode != http.StatusUnauthorized { t.Logf("case %v", r) t.Errorf("Expected status %v, but got %v", http.StatusUnauthorized, resp.StatusCode) b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) } }() } }
func TestAuthModeAlwaysDeny(t *testing.T) { framework.DeleteAllEtcdKeys() // Set up a master helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysDenyAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), }) transport := http.DefaultTransport for _, r := range getTestRequests() { bodyBytes := bytes.NewReader([]byte(r.body)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } func() { resp, err := transport.RoundTrip(req) defer resp.Body.Close() if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } if resp.StatusCode != http.StatusForbidden { t.Logf("case %v", r) t.Errorf("Expected status Forbidden but got status %v", resp.Status) } }() } }
func TestUnschedulableNodes(t *testing.T) { helper, err := framework.NewHelper() if err != nil { t.Fatalf("Couldn't create etcd helper: %v", err) } framework.DeleteAllEtcdKeys() var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), }) restClient := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Version()}) schedulerConfigFactory := factory.NewConfigFactory(restClient) schedulerConfig, err := schedulerConfigFactory.Create() if err != nil { t.Fatalf("Couldn't create scheduler config: %v", err) } eventBroadcaster := record.NewBroadcaster() schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"}) eventBroadcaster.StartRecordingToSink(restClient.Events("")) scheduler.New(schedulerConfig).Run() defer close(schedulerConfig.StopEverything) DoTestUnschedulableNodes(t, restClient, schedulerConfigFactory.NodeLister.Store) }
// TODO: Merge this into startMasterOrDie. func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) { helper, err := master.NewEtcdHelper(NewEtcdClient(), testapi.Version(), etcdtest.PathPrefix()) if err != nil { t.Fatalf("unexpected error: %v", err) } m := master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableLogsSupport: false, EnableProfiling: true, EnableUISupport: false, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), }) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) return m, s }
// TestKindAuthorization tests that authorization can be controlled // by namespace. func TestKindAuthorization(t *testing.T) { framework.DeleteAllEtcdKeys() // This file has alice and bob in it. // Set up a master helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } a := newAuthorizerWithContents(t, `{"resource": "services"} `) var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authenticator: getTestTokenAuth(), Authorizer: a, AdmissionControl: admit.NewAlwaysAdmit(), }) previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport requests := []struct { verb string URL string body string statusCodes map[int]bool // allowed status codes. }{ {"POST", timeoutPath("services", api.NamespaceDefault, ""), aService, code201}, {"GET", path("services", api.NamespaceDefault, ""), "", code200}, {"GET", path("services", api.NamespaceDefault, "a"), "", code200}, {"DELETE", timeoutPath("services", api.NamespaceDefault, "a"), "", code200}, {"POST", timeoutPath("pods", api.NamespaceDefault, ""), aPod, code403}, {"GET", path("pods", "", ""), "", code403}, {"GET", path("pods", api.NamespaceDefault, "a"), "", code403}, {"DELETE", timeoutPath("pods", api.NamespaceDefault, "a"), "", code403}, } for _, r := range requests { token := BobToken var bodyStr string if r.body != "" { bodyStr = fmt.Sprintf(r.body, "") if r.verb == "PUT" && r.body != "" { // For update operations, insert previous resource version if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion) bodyStr = fmt.Sprintf(r.body, resourceVersionJson) } } } r.body = bodyStr bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) { resp, err := transport.RoundTrip(req) defer resp.Body.Close() if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Logf("case %v", r) t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) t.Errorf("Body: %v", string(b)) } else { if r.verb == "POST" { // For successful create operations, extract resourceVersion id, currentResourceVersion, err := parseResourceVersion(b) if err == nil { key := getPreviousResourceVersionKey(r.URL, id) previousResourceVersion[key] = currentResourceVersion } } } } } }
// TestAliceNotForbiddenOrUnauthorized tests a user who is known to // the authentication system and authorized to do any actions. func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { framework.DeleteAllEtcdKeys() // This file has alice and bob in it. // Set up a master helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authenticator: getTestTokenAuth(), Authorizer: allowAliceAuthorizer{}, AdmissionControl: admit.NewAlwaysAdmit(), }) previousResourceVersion := make(map[string]float64) transport := http.DefaultTransport for _, r := range getTestRequests() { token := AliceToken var bodyStr string if r.body != "" { sub := "" if r.verb == "PUT" { // For update operations, insert previous resource version if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion) } namespace := "default" sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace) } bodyStr = fmt.Sprintf(r.body, sub) } r.body = bodyStr bodyBytes := bytes.NewReader([]byte(bodyStr)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) if r.verb == "PATCH" { req.Header.Set("Content-Type", "application/merge-patch+json") } func() { resp, err := transport.RoundTrip(req) defer resp.Body.Close() if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } b, _ := ioutil.ReadAll(resp.Body) if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Logf("case %v", r) t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) t.Errorf("Body: %v", string(b)) } else { if r.verb == "POST" { // For successful create operations, extract resourceVersion id, currentResourceVersion, err := parseResourceVersion(b) if err == nil { key := getPreviousResourceVersionKey(r.URL, id) previousResourceVersion[key] = currentResourceVersion } } } }() } }
// TestReadOnlyAuthorization tests that authorization can be controlled // by namespace. func TestReadOnlyAuthorization(t *testing.T) { framework.DeleteAllEtcdKeys() // This file has alice and bob in it. // Set up a master helper, err := framework.NewHelper() if err != nil { t.Fatalf("unexpected error: %v", err) } a := newAuthorizerWithContents(t, `{"readonly": true}`) var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() m = master.New(&master.Config{ EtcdHelper: helper, QingletClient: client.FakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authenticator: getTestTokenAuth(), Authorizer: a, AdmissionControl: admit.NewAlwaysAdmit(), }) transport := http.DefaultTransport requests := []struct { verb string URL string body string statusCodes map[int]bool // allowed status codes. }{ {"POST", path("pods", "", ""), aPod, code403}, {"GET", path("pods", "", ""), "", code200}, {"GET", path("pods", api.NamespaceDefault, "a"), "", code404}, } for _, r := range requests { token := BobToken bodyBytes := bytes.NewReader([]byte(r.body)) req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) if err != nil { t.Fatalf("unexpected error: %v", err) } req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) func() { resp, err := transport.RoundTrip(req) defer resp.Body.Close() if err != nil { t.Logf("case %v", r) t.Fatalf("unexpected error: %v", err) } if _, ok := r.statusCodes[resp.StatusCode]; !ok { t.Logf("case %v", r) t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) b, _ := ioutil.ReadAll(resp.Body) t.Errorf("Body: %v", string(b)) } }() } }
func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (string, string) { // Setup servers := []string{} glog.Infof("Creating etcd client pointing to %v", servers) handler := delegateHandler{} apiServer := httptest.NewServer(&handler) etcdClient := etcd.NewClient(servers) sleep := 4 * time.Second ok := false for i := 0; i < 3; i++ { keys, err := etcdClient.Get("/", false, false) if err != nil { glog.Warningf("Unable to list root etcd keys: %v", err) if i < 2 { time.Sleep(sleep) sleep = sleep * sleep } continue } for _, node := range keys.Node.Nodes { if _, err := etcdClient.Delete(node.Key, true); err != nil { glog.Fatalf("Unable delete key: %v", err) } } ok = true break } if !ok { glog.Fatalf("Failed to connect to etcd") } cl := client.NewOrDie(&client.Config{Host: apiServer.URL, Version: apiVersion}) helper, err := master.NewEtcdHelper(etcdClient, "", etcdtest.PathPrefix()) if err != nil { glog.Fatalf("Unable to get etcd helper: %v", err) } // Master host, port, err := net.SplitHostPort(strings.TrimLeft(apiServer.URL, "http://")) if err != nil { glog.Fatalf("Unable to parse URL '%v': %v", apiServer.URL, err) } portNumber, err := strconv.Atoi(port) if err != nil { glog.Fatalf("Nonnumeric port? %v", err) } publicAddress := net.ParseIP(host) if publicAddress == nil { glog.Fatalf("no public address for %s", host) } // Create a master and install handlers into mux. m := master.New(&master.Config{ EtcdHelper: helper, QingletClient: fakeQingletClient{}, EnableCoreControllers: true, EnableLogsSupport: false, EnableProfiling: true, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), ReadWritePort: portNumber, PublicAddress: publicAddress, CacheTimeout: 2 * time.Second, }) handler.delegate = m.Handler // Scheduler schedulerConfigFactory := factory.NewConfigFactory(cl) schedulerConfig, err := schedulerConfigFactory.Create() if err != nil { glog.Fatalf("Couldn't create scheduler config: %v", err) } eventBroadcaster := record.NewBroadcaster() schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"}) eventBroadcaster.StartLogging(glog.Infof) eventBroadcaster.StartRecordingToSink(cl.Events("")) scheduler.New(schedulerConfig).Run() endpoints := service.NewEndpointController(cl) // ensure the service endpoints are sync'd several times within the window that the integration tests wait go endpoints.Run(3, util.NeverStop) controllerManager := replicationControllerPkg.NewReplicationManager(cl, replicationControllerPkg.BurstReplicas) // TODO: Write an integration test for the replication controllers watch. go controllerManager.Run(3, util.NeverStop) nodeController := nodecontroller.NewNodeController(nil, cl, 10, 5*time.Minute, nodecontroller.NewPodEvictor(util.NewFakeRateLimiter()), 40*time.Second, 60*time.Second, 5*time.Second, nil, false) nodeController.Run(5 * time.Second) cadvisorInterface := new(cadvisor.Fake) // Qinglet (localhost) testRootDir := makeTempDirOrDie("qinglet_integ_1.", "") configFilePath := makeTempDirOrDie("config", testRootDir) glog.Infof("Using %s as root dir for qinglet #1", testRootDir) fakeDocker1.VersionInfo = docker.Env{"ApiVersion=1.15"} kcfg := qingletapp.SimpleQinglet(cl, &fakeDocker1, "localhost", testRootDir, firstManifestURL, "127.0.0.1", 10250, api.NamespaceDefault, empty_dir.ProbeVolumePlugins(), nil, cadvisorInterface, configFilePath, nil, qingcontainer.FakeOS{}) qingletapp.RunQinglet(kcfg, nil) // Qinglet (machine) // Create a second qinglet so that the guestbook example's two redis slaves both // have a place they can schedule. testRootDir = makeTempDirOrDie("qinglet_integ_2.", "") glog.Infof("Using %s as root dir for qinglet #2", testRootDir) fakeDocker2.VersionInfo = docker.Env{"ApiVersion=1.15"} kcfg = qingletapp.SimpleQinglet(cl, &fakeDocker2, "127.0.0.1", testRootDir, secondManifestURL, "127.0.0.1", 10251, api.NamespaceDefault, empty_dir.ProbeVolumePlugins(), nil, cadvisorInterface, "", nil, qingcontainer.FakeOS{}) qingletapp.RunQinglet(kcfg, nil) return apiServer.URL, configFilePath }