// Index returns a list of items that match on the index function // Index is thread-safe so long as you treat all items as immutable func (c *threadSafeMap) Index(indexName string, obj interface{}) ([]interface{}, error) { c.lock.RLock() defer c.lock.RUnlock() indexFunc := c.indexers[indexName] if indexFunc == nil { return nil, fmt.Errorf("Index with name %s does not exist", indexName) } indexKeys, err := indexFunc(obj) if err != nil { return nil, err } index := c.indices[indexName] // need to de-dupe the return list. Since multiple keys are allowed, this can happen. returnKeySet := sets.String{} for _, indexKey := range indexKeys { set := index[indexKey] for _, key := range set.List() { returnKeySet.Insert(key) } } list := make([]interface{}, 0, returnKeySet.Len()) for absoluteKey := range returnKeySet { list = append(list, c.items[absoluteKey]) } return list, nil }
// dialFromNode executes a tcp or udp request based on protocol via kubectl exec // in a test container running with host networking. // - minTries is the minimum number of curl attempts required before declaring // success. Set to 0 if you'd like to return as soon as all endpoints respond // at least once. // - maxTries is the maximum number of curl attempts. If this many attempts pass // and we don't see all expected endpoints, the test fails. // maxTries == minTries will confirm that we see the expected endpoints and no // more for maxTries. Use this if you want to eg: fail a readiness check on a // pod and confirm it doesn't show up as an endpoint. func (config *NetworkingTestConfig) dialFromNode(protocol, targetIP string, targetPort, maxTries, minTries int, expectedEps sets.String) { var cmd string if protocol == "udp" { cmd = fmt.Sprintf("echo 'hostName' | timeout -t 3 nc -w 1 -u %s %d", targetIP, targetPort) } else { cmd = fmt.Sprintf("curl -q -s --connect-timeout 1 http://%s:%d/hostName", targetIP, targetPort) } // TODO: This simply tells us that we can reach the endpoints. Check that // the probability of hitting a specific endpoint is roughly the same as // hitting any other. eps := sets.NewString() filterCmd := fmt.Sprintf("%s | grep -v '^\\s*$'", cmd) for i := 0; i < maxTries; i++ { stdout, err := framework.RunHostCmd(config.ns, config.hostTestContainerPod.Name, filterCmd) if err != nil { // A failure to kubectl exec counts as a try, not a hard fail. // Also note that we will keep failing for maxTries in tests where // we confirm unreachability. framework.Logf("Failed to execute %v: %v", filterCmd, err) } else { eps.Insert(strings.TrimSpace(stdout)) } framework.Logf("Waiting for %+v endpoints, got endpoints %+v", expectedEps.Difference(eps), eps) // Check against i+1 so we exit if minTries == maxTries. if (eps.Equal(expectedEps) || eps.Len() == 0 && expectedEps.Len() == 0) && i+1 >= minTries { return } } config.diagnoseMissingEndpoints(eps) framework.Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) }
func runResourceTrackingTest(f *framework.Framework, podsPerNode int, nodeNames sets.String, rm *framework.ResourceMonitor, expectedCPU map[string]map[float64]float64, expectedMemory framework.ResourceUsagePerContainer) { numNodes := nodeNames.Len() totalPods := podsPerNode * numNodes By(fmt.Sprintf("Creating a RC of %d pods and wait until all pods of this RC are running", totalPods)) rcName := fmt.Sprintf("resource%d-%s", totalPods, string(uuid.NewUUID())) // TODO: Use a more realistic workload Expect(framework.RunRC(testutils.RCConfig{ Client: f.ClientSet, InternalClient: f.InternalClientset, Name: rcName, Namespace: f.Namespace.Name, Image: framework.GetPauseImageName(f.ClientSet), Replicas: totalPods, })).NotTo(HaveOccurred()) // Log once and flush the stats. rm.LogLatest() rm.Reset() By("Start monitoring resource usage") // Periodically dump the cpu summary until the deadline is met. // Note that without calling framework.ResourceMonitor.Reset(), the stats // would occupy increasingly more memory. This should be fine // for the current test duration, but we should reclaim the // entries if we plan to monitor longer (e.g., 8 hours). deadline := time.Now().Add(monitoringTime) for time.Now().Before(deadline) { timeLeft := deadline.Sub(time.Now()) framework.Logf("Still running...%v left", timeLeft) if timeLeft < reportingPeriod { time.Sleep(timeLeft) } else { time.Sleep(reportingPeriod) } logPodsOnNodes(f.ClientSet, nodeNames.List()) } By("Reporting overall resource usage") logPodsOnNodes(f.ClientSet, nodeNames.List()) usageSummary, err := rm.GetLatest() Expect(err).NotTo(HaveOccurred()) // TODO(random-liu): Remove the original log when we migrate to new perfdash framework.Logf("%s", rm.FormatResourceUsage(usageSummary)) // Log perf result framework.PrintPerfData(framework.ResourceUsageToPerfData(rm.GetMasterNodeLatest(usageSummary))) verifyMemoryLimits(f.ClientSet, expectedMemory, usageSummary) cpuSummary := rm.GetCPUSummary() framework.Logf("%s", rm.FormatCPUSummary(cpuSummary)) // Log perf result framework.PrintPerfData(framework.CPUUsageToPerfData(rm.GetMasterNodeCPUSummary(cpuSummary))) verifyCPULimits(expectedCPU, cpuSummary) By("Deleting the RC") framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.Namespace.Name, rcName) }
func runResourceTrackingTest(framework *Framework, podsPerNode int, nodeNames sets.String, rm *resourceMonitor, expected map[string]map[float64]float64) { numNodes := nodeNames.Len() totalPods := podsPerNode * numNodes By(fmt.Sprintf("Creating a RC of %d pods and wait until all pods of this RC are running", totalPods)) rcName := fmt.Sprintf("resource%d-%s", totalPods, string(util.NewUUID())) // TODO: Use a more realistic workload Expect(RunRC(RCConfig{ Client: framework.Client, Name: rcName, Namespace: framework.Namespace.Name, Image: "gcr.io/google_containers/pause:2.0", Replicas: totalPods, })).NotTo(HaveOccurred()) // Log once and flush the stats. rm.LogLatest() rm.Reset() By("Start monitoring resource usage") // Periodically dump the cpu summary until the deadline is met. // Note that without calling resourceMonitor.Reset(), the stats // would occupy increasingly more memory. This should be fine // for the current test duration, but we should reclaim the // entries if we plan to monitor longer (e.g., 8 hours). deadline := time.Now().Add(monitoringTime) for time.Now().Before(deadline) { timeLeft := deadline.Sub(time.Now()) Logf("Still running...%v left", timeLeft) if timeLeft < reportingPeriod { time.Sleep(timeLeft) } else { time.Sleep(reportingPeriod) } logPodsOnNodes(framework.Client, nodeNames.List()) } By("Reporting overall resource usage") logPodsOnNodes(framework.Client, nodeNames.List()) rm.LogLatest() usageSummary, err := rm.GetLatest() Expect(err).NotTo(HaveOccurred()) Logf("%s", rm.FormatResourceUsage(usageSummary)) // TODO(yujuhong): Set realistic values after gathering enough data. verifyMemoryLimits(resourceUsagePerContainer{ "/kubelet": &containerResourceUsage{MemoryRSSInBytes: 500 * 1024 * 1024}, "/docker-daemon": &containerResourceUsage{MemoryRSSInBytes: 500 * 1024 * 1024}, }, usageSummary) cpuSummary := rm.GetCPUSummary() Logf("%s", rm.FormatCPUSummary(cpuSummary)) verifyCPULimits(expected, cpuSummary) By("Deleting the RC") DeleteRC(framework.Client, framework.Namespace.Name, rcName) }
// getAssigneesAndUnassignees checks to see when someone comments "/assign" or "/unassign" // returns two sets.String // 1. github handles to be assigned // 2. github handles to be unassigned // Note* Could possibly assign directly in the function call, but easier to test if function returns toAssign, toUnassign func (h *AssignUnassignHandler) getAssigneesAndUnassignees(obj *github.MungeObject, comments []*githubapi.IssueComment, fileList []*githubapi.CommitFile, potentialOwners weightMap) (toAssign, toUnassign sets.String) { toAssign = sets.String{} toUnassign = sets.String{} assignComments := c.FilterComments(comments, c.CommandName(assignCommand)) unassignComments := c.FilterComments(comments, c.CommandName(unassignCommand)) invalidUsers := sets.String{} //collect all the people that should be assigned for _, cmt := range assignComments { if isValidReviewer(potentialOwners, cmt.User) { obj.DeleteComment(cmt) toAssign.Insert(*cmt.User.Login) } else { // build the set of people who asked to be assigned but aren't in reviewers // use the @ as a prefix so github notifies invalid users invalidUsers.Insert("@" + *cmt.User.Login) } } // collect all the people that should be unassigned for _, cmt := range unassignComments { if isAssignee(obj.Issue.Assignees, cmt.User) { obj.DeleteComment(cmt) toUnassign.Insert(*cmt.User.Login) } } // Create a notification if someone tried to self assign, but could not because they weren't in the owners files if invalidUsers.Len() != 0 { previousNotifications := c.FilterComments(comments, c.MungerNotificationName(invalidReviewer)) if assignComments.Empty() || (!previousNotifications.Empty() && previousNotifications.GetLast().CreatedAt.After(*assignComments.GetLast().CreatedAt)) { // if there were no assign comments, no need to notify // if the last notification happened after the last assign comment, no need to notify again return toAssign, toUnassign } if !previousNotifications.Empty() { for _, c := range previousNotifications { obj.DeleteComment(c) } } context := bytes.NewBufferString("The following people cannot be assigned because they are not in the OWNERS files\n") for user := range invalidUsers { context.WriteString(fmt.Sprintf("- %s\n", user)) } context.WriteString("\n") c.Notification{Name: invalidReviewer, Arguments: "", Context: context.String()}.Post(obj) } return toAssign, toUnassign }
func validateList(t *testing.T, lister Lister, user user.Info, expectedSet sets.String) { namespaceList, err := lister.List(user) if err != nil { t.Errorf("Unexpected error %v", err) } results := sets.String{} for _, namespace := range namespaceList.Items { results.Insert(namespace.Name) } if results.Len() != expectedSet.Len() || !results.HasAll(expectedSet.List()...) { t.Errorf("User %v, Expected: %v, Actual: %v", user.GetName(), expectedSet, results) } }
func runResourceTrackingTest(framework *Framework, podsPerNode int, nodeNames sets.String, resourceMonitor *resourceMonitor) { numNodes := nodeNames.Len() totalPods := podsPerNode * numNodes By(fmt.Sprintf("Creating a RC of %d pods and wait until all pods of this RC are running", totalPods)) rcName := fmt.Sprintf("resource%d-%s", totalPods, string(util.NewUUID())) // TODO: Use a more realistic workload Expect(RunRC(RCConfig{ Client: framework.Client, Name: rcName, Namespace: framework.Namespace.Name, Image: "gcr.io/google_containers/pause:go", Replicas: totalPods, })).NotTo(HaveOccurred()) // Log once and flush the stats. resourceMonitor.LogLatest() resourceMonitor.Reset() By("Start monitoring resource usage") // Periodically dump the cpu summary until the deadline is met. // Note that without calling resourceMonitor.Reset(), the stats // would occupy increasingly more memory. This should be fine // for the current test duration, but we should reclaim the // entries if we plan to monitor longer (e.g., 8 hours). deadline := time.Now().Add(monitoringTime) for time.Now().Before(deadline) { Logf("Still running...%v left", deadline.Sub(time.Now())) time.Sleep(reportingPeriod) timeLeft := deadline.Sub(time.Now()) Logf("Still running...%v left", timeLeft) if timeLeft < reportingPeriod { time.Sleep(timeLeft) } else { time.Sleep(reportingPeriod) } logPodsOnNodes(framework.Client, nodeNames.List()) } By("Reporting overall resource usage") logPodsOnNodes(framework.Client, nodeNames.List()) resourceMonitor.LogCPUSummary() resourceMonitor.LogLatest() By("Deleting the RC") DeleteRC(framework.Client, framework.Namespace.Name, rcName) }
// DialFromContainers executes a curl via kubectl exec in a test container, // which might then translate to a tcp or udp request based on the protocol // argument in the url. // - minTries is the minimum number of curl attempts required before declaring // success. Set to 0 if you'd like to return as soon as all endpoints respond // at least once. // - maxTries is the maximum number of curl attempts. If this many attempts pass // and we don't see all expected endpoints, the test fails. // - expectedEps is the set of endpointnames to wait for. Typically this is also // the hostname reported by each pod in the service through /hostName. // maxTries == minTries will confirm that we see the expected endpoints and no // more for maxTries. Use this if you want to eg: fail a readiness check on a // pod and confirm it doesn't show up as an endpoint. func (config *NetworkingTestConfig) DialFromContainer(protocol, containerIP, targetIP string, containerHttpPort, targetPort, maxTries, minTries int, expectedEps sets.String) { cmd := fmt.Sprintf("curl -q -s 'http://%s:%d/dial?request=hostName&protocol=%s&host=%s&port=%d&tries=1'", containerIP, containerHttpPort, protocol, targetIP, targetPort) eps := sets.NewString() for i := 0; i < maxTries; i++ { stdout, stderr, err := config.f.ExecShellInPodWithFullOutput(config.HostTestContainerPod.Name, cmd) if err != nil { // A failure to kubectl exec counts as a try, not a hard fail. // Also note that we will keep failing for maxTries in tests where // we confirm unreachability. Logf("Failed to execute %q: %v, stdout: %q, stderr %q", cmd, err, stdout, stderr) } else { var output map[string][]string if err := json.Unmarshal([]byte(stdout), &output); err != nil { Logf("WARNING: Failed to unmarshal curl response. Cmd %v run in %v, output: %s, err: %v", cmd, config.HostTestContainerPod.Name, stdout, err) continue } for _, hostName := range output["responses"] { trimmed := strings.TrimSpace(hostName) if trimmed != "" { eps.Insert(trimmed) } } } Logf("Waiting for endpoints: %v", expectedEps.Difference(eps)) // Check against i+1 so we exit if minTries == maxTries. if (eps.Equal(expectedEps) || eps.Len() == 0 && expectedEps.Len() == 0) && i+1 >= minTries { return } // TODO: get rid of this delay #36281 time.Sleep(hitEndpointRetryDelay) } config.diagnoseMissingEndpoints(eps) Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) }
// DialFromNode executes a tcp or udp request based on protocol via kubectl exec // in a test container running with host networking. // - minTries is the minimum number of curl attempts required before declaring // success. Set to 0 if you'd like to return as soon as all endpoints respond // at least once. // - maxTries is the maximum number of curl attempts. If this many attempts pass // and we don't see all expected endpoints, the test fails. // maxTries == minTries will confirm that we see the expected endpoints and no // more for maxTries. Use this if you want to eg: fail a readiness check on a // pod and confirm it doesn't show up as an endpoint. func (config *NetworkingTestConfig) DialFromNode(protocol, targetIP string, targetPort, maxTries, minTries int, expectedEps sets.String) { var cmd string if protocol == "udp" { // TODO: It would be enough to pass 1s+epsilon to timeout, but unfortunately // busybox timeout doesn't support non-integer values. cmd = fmt.Sprintf("echo 'hostName' | timeout -t 2 nc -w 1 -u %s %d", targetIP, targetPort) } else { cmd = fmt.Sprintf("timeout -t 15 curl -q -s --connect-timeout 1 http://%s:%d/hostName", targetIP, targetPort) } // TODO: This simply tells us that we can reach the endpoints. Check that // the probability of hitting a specific endpoint is roughly the same as // hitting any other. eps := sets.NewString() filterCmd := fmt.Sprintf("%s | grep -v '^\\s*$'", cmd) for i := 0; i < maxTries; i++ { stdout, stderr, err := config.f.ExecShellInPodWithFullOutput(config.HostTestContainerPod.Name, filterCmd) if err != nil || len(stderr) > 0 { // A failure to exec command counts as a try, not a hard fail. // Also note that we will keep failing for maxTries in tests where // we confirm unreachability. Logf("Failed to execute %q: %v, stdout: %q, stderr: %q", filterCmd, err, stdout, stderr) } else { trimmed := strings.TrimSpace(stdout) if trimmed != "" { eps.Insert(trimmed) } } Logf("Waiting for %+v endpoints, got endpoints %+v", expectedEps.Difference(eps), eps) // Check against i+1 so we exit if minTries == maxTries. if (eps.Equal(expectedEps) || eps.Len() == 0 && expectedEps.Len() == 0) && i+1 >= minTries { return } // TODO: get rid of this delay #36281 time.Sleep(hitEndpointRetryDelay) } config.diagnoseMissingEndpoints(eps) Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) }
// redirectURIsFromRoutes is the namesToObjMapperFunc specific to Routes. // Returns a map of route name to redirect URIs that contain the default data as specified by the route's ingresses. func (a *saOAuthClientAdapter) redirectURIsFromRoutes(namespace string, osRouteNames sets.String) map[string]redirectURIList { var routes []routeapi.Route routeInterface := a.routeClient.Routes(namespace) if osRouteNames.Len() > 1 { if r, err := routeInterface.List(kapi.ListOptions{}); err == nil { routes = r.Items } } else { if r, err := routeInterface.Get(osRouteNames.List()[0]); err == nil { routes = append(routes, *r) } } routeMap := map[string]redirectURIList{} for _, route := range routes { if osRouteNames.Has(route.Name) { routeMap[route.Name] = redirectURIsFromRoute(&route) } } return routeMap }
func getPotentialOwners(author string, feats *features.Features, files []*githubapi.CommitFile, leafOnly bool) (weightMap, int64) { potentialOwners := weightMap{} weightSum := int64(0) aliases := feats.Aliases var fileOwners sets.String for _, file := range files { if file == nil { continue } fileWeight := int64(1) if file.Changes != nil && *file.Changes != 0 { fileWeight = int64(*file.Changes) } // Judge file size on a log scale-- effectively this // makes three buckets, we shouldn't have many 10k+ // line changes. fileWeight = int64(math.Log10(float64(fileWeight))) + 1 if leafOnly { fileOwners = feats.Repos.LeafReviewers(*file.Filename) } else { fileOwners = feats.Repos.Reviewers(*file.Filename) } if fileOwners.Len() == 0 { glog.Warningf("Couldn't find an owner for: %s", *file.Filename) } if aliases != nil && aliases.IsEnabled { fileOwners = aliases.Expand(fileOwners) } for _, owner := range fileOwners.List() { if owner == author { continue } potentialOwners[owner] = potentialOwners[owner] + fileWeight weightSum += fileWeight } } return potentialOwners, weightSum }
// dialFromContainers executes a curl via kubectl exec in a test container, // which might then translate to a tcp or udp request based on the protocol // argument in the url. // - minTries is the minimum number of curl attempts required before declaring // success. Set to 0 if you'd like to return as soon as all endpoints respond // at least once. // - maxTries is the maximum number of curl attempts. If this many attempts pass // and we don't see all expected endpoints, the test fails. // - expectedEps is the set of endpointnames to wait for. Typically this is also // the hostname reported by each pod in the service through /hostName. // maxTries == minTries will confirm that we see the expected endpoints and no // more for maxTries. Use this if you want to eg: fail a readiness check on a // pod and confirm it doesn't show up as an endpoint. func (config *NetworkingTestConfig) dialFromContainer(protocol, containerIP, targetIP string, containerHttpPort, targetPort, maxTries, minTries int, expectedEps sets.String) { cmd := fmt.Sprintf("curl -q -s 'http://%s:%d/dial?request=hostName&protocol=%s&host=%s&port=%d&tries=1'", containerIP, containerHttpPort, protocol, targetIP, targetPort) eps := sets.NewString() for i := 0; i < maxTries; i++ { stdout, err := framework.RunHostCmd(config.ns, config.hostTestContainerPod.Name, cmd) if err != nil { // A failure to kubectl exec counts as a try, not a hard fail. // Also note that we will keep failing for maxTries in tests where // we confirm unreachability. framework.Logf("Failed to execute %v: %v", cmd, err) } else { var output map[string][]string if err := json.Unmarshal([]byte(stdout), &output); err != nil { framework.Logf("WARNING: Failed to unmarshal curl response. Cmd %v run in %v, output: %s, err: %v", cmd, config.hostTestContainerPod.Name, stdout, err) continue } for _, hostName := range output["responses"] { eps.Insert(hostName) } } framework.Logf("Waiting for endpoints: %v", expectedEps.Difference(eps)) // Check against i+1 so we exit if minTries == maxTries. if (eps.Equal(expectedEps) || eps.Len() == 0 && expectedEps.Len() == 0) && i+1 >= minTries { return } } config.diagnoseMissingEndpoints(eps) framework.Failf("Failed to find expected endpoints:\nTries %d\nCommand %v\nretrieved %v\nexpected %v\n", minTries, cmd, eps, expectedEps) }
func TestHammerController(t *testing.T) { // This test executes a bunch of requests through the fake source and // controller framework to make sure there's no locking/threading // errors. If an error happens, it should hang forever or trigger the // race detector. // source simulates an apiserver object endpoint. source := framework.NewFakeControllerSource() // Let's do threadsafe output to get predictable test results. outputSetLock := sync.Mutex{} // map of key to operations done on the key outputSet := map[string][]string{} recordFunc := func(eventType string, obj interface{}) { key, err := framework.DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { t.Errorf("something wrong with key: %v", err) key = "oops something went wrong with the key" } // Record some output when items are deleted. outputSetLock.Lock() defer outputSetLock.Unlock() outputSet[key] = append(outputSet[key], eventType) } // Make a controller which just logs all the changes it gets. _, controller := framework.NewInformer( source, &api.Pod{}, time.Millisecond*100, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { recordFunc("add", obj) }, UpdateFunc: func(oldObj, newObj interface{}) { recordFunc("update", newObj) }, DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) }, }, ) if controller.HasSynced() { t.Errorf("Expected HasSynced() to return false before we started the controller") } // Run the controller and run it until we close stop. stop := make(chan struct{}) go controller.Run(stop) // Let's wait for the controller to do its initial sync time.Sleep(100 * time.Millisecond) if !controller.HasSynced() { t.Errorf("Expected HasSynced() to return true after the initial sync") } wg := sync.WaitGroup{} const threads = 3 wg.Add(threads) for i := 0; i < threads; i++ { go func() { defer wg.Done() // Let's add a few objects to the source. currentNames := sets.String{} rs := rand.NewSource(rand.Int63()) f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs) r := rand.New(rs) // Mustn't use r and f concurrently! for i := 0; i < 100; i++ { var name string var isNew bool if currentNames.Len() == 0 || r.Intn(3) == 1 { f.Fuzz(&name) isNew = true } else { l := currentNames.List() name = l[r.Intn(len(l))] } pod := &api.Pod{} f.Fuzz(pod) pod.ObjectMeta.Name = name pod.ObjectMeta.Namespace = "default" // Add, update, or delete randomly. // Note that these pods are not valid-- the fake source doesn't // call validation or perform any other checking. if isNew { currentNames.Insert(name) source.Add(pod) continue } switch r.Intn(2) { case 0: currentNames.Insert(name) source.Modify(pod) case 1: currentNames.Delete(name) source.Delete(pod) } } }() } wg.Wait() // Let's wait for the controller to finish processing the things we just added. time.Sleep(100 * time.Millisecond) close(stop) outputSetLock.Lock() t.Logf("got: %#v", outputSet) }