func (t *tcShaper) nextClassID() (int, error) { data, err := t.e.Command("tc", "class", "show", "dev", t.iface).CombinedOutput() if err != nil { return -1, err } scanner := bufio.NewScanner(bytes.NewBuffer(data)) classes := sets.String{} for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) // skip empty lines if len(line) == 0 { continue } parts := strings.Split(line, " ") // expected tc line: // class htb 1:1 root prio 0 rate 1000Kbit ceil 1000Kbit burst 1600b cburst 1600b if len(parts) != 14 { return -1, fmt.Errorf("unexpected output from tc: %s (%v)", scanner.Text(), parts) } classes.Insert(parts[2]) } // Make sure it doesn't go forever for nextClass := 1; nextClass < 10000; nextClass++ { if !classes.Has(fmt.Sprintf("1:%d", nextClass)) { return nextClass, nil } } // This should really never happen return -1, fmt.Errorf("exhausted class space, please try again") }
// Test public interface func doTestIndex(t *testing.T, indexer Indexer) { mkObj := func(id string, val string) testStoreObject { return testStoreObject{id: id, val: val} } // Test Index expected := map[string]sets.String{} expected["b"] = sets.NewString("a", "c") expected["f"] = sets.NewString("e") expected["h"] = sets.NewString("g") indexer.Add(mkObj("a", "b")) indexer.Add(mkObj("c", "b")) indexer.Add(mkObj("e", "f")) indexer.Add(mkObj("g", "h")) { for k, v := range expected { found := sets.String{} indexResults, err := indexer.Index("by_val", mkObj("", k)) if err != nil { t.Errorf("Unexpected error %v", err) } for _, item := range indexResults { found.Insert(item.(testStoreObject).id) } items := v.List() if !found.HasAll(items...) { t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List()) } } } }
// 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.UnsortedList() { returnKeySet.Insert(key) } } list := make([]interface{}, 0, returnKeySet.Len()) for absoluteKey := range returnKeySet { list = append(list, c.items[absoluteKey]) } return list, nil }
func (r *Requirement) Values() sets.String { ret := sets.String{} for i := range r.strValues { ret.Insert(r.strValues[i]) } return ret }
// NegotiateVersion queries the server's supported api versions to find // a version that both client and server support. // - If no version is provided, try registered client versions in order of // preference. // - If version is provided and the server does not support it, // return an error. func NegotiateVersion(client DiscoveryInterface, requiredGV *schema.GroupVersion, clientRegisteredGVs []schema.GroupVersion) (*schema.GroupVersion, error) { clientVersions := sets.String{} for _, gv := range clientRegisteredGVs { clientVersions.Insert(gv.String()) } groups, err := client.ServerGroups() if err != nil { // This is almost always a connection error, and higher level code should treat this as a generic error, // not a negotiation specific error. return nil, err } versions := metav1.ExtractGroupVersions(groups) serverVersions := sets.String{} for _, v := range versions { serverVersions.Insert(v) } // If version explicitly requested verify that both client and server support it. // If server does not support warn, but try to negotiate a lower version. if requiredGV != nil { if !clientVersions.Has(requiredGV.String()) { return nil, fmt.Errorf("client does not support API version %q; client supported API versions: %v", requiredGV, clientVersions) } // If the server supports no versions, then we should just use the preferredGV // This can happen because discovery fails due to 403 Forbidden errors if len(serverVersions) == 0 { return requiredGV, nil } if serverVersions.Has(requiredGV.String()) { return requiredGV, nil } // If we are using an explicit config version the server does not support, fail. return nil, fmt.Errorf("server does not support API version %q", requiredGV) } for _, clientGV := range clientRegisteredGVs { if serverVersions.Has(clientGV.String()) { // Version was not explicitly requested in command config (--api-version). // Ok to fall back to a supported version with a warning. // TODO: caesarxuchao: enable the warning message when we have // proper fix. Please refer to issue #14895. // if len(version) != 0 { // glog.Warningf("Server does not support API version '%s'. Falling back to '%s'.", version, clientVersion) // } t := clientGV return &t, nil } } // if we have no server versions and we have no required version, choose the first clientRegisteredVersion if len(serverVersions) == 0 && len(clientRegisteredGVs) > 0 { return &clientRegisteredGVs[0], nil } return nil, fmt.Errorf("failed to negotiate an api version; server supports: %v, client supports: %v", serverVersions, clientVersions) }
// GetNamespacesFromPodAffinityTerm returns a set of names // according to the namespaces indicated in podAffinityTerm. // 1. If the namespaces is nil considers the given pod's namespace // 2. If the namespaces is empty list then considers all the namespaces func GetNamespacesFromPodAffinityTerm(pod *v1.Pod, podAffinityTerm *v1.PodAffinityTerm) sets.String { names := sets.String{} if podAffinityTerm.Namespaces == nil { names.Insert(pod.Namespace) } else if len(podAffinityTerm.Namespaces) != 0 { names.Insert(podAffinityTerm.Namespaces...) } return names }
func ExampleNewInformer() { // source simulates an apiserver object endpoint. source := fcache.NewFakeControllerSource() // Let's do threadsafe output to get predictable test results. deletionCounter := make(chan string, 1000) // Make a controller that immediately deletes anything added to it, and // logs anything deleted. _, controller := NewInformer( source, &v1.Pod{}, time.Millisecond*100, ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { source.Delete(obj.(runtime.Object)) }, DeleteFunc: func(obj interface{}) { key, err := DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { key = "oops something went wrong with the key" } // Report this deletion. deletionCounter <- key }, }, ) // Run the controller and run it until we close stop. stop := make(chan struct{}) defer close(stop) go controller.Run(stop) // Let's add a few objects to the source. testIDs := []string{"a-hello", "b-controller", "c-framework"} for _, name := range testIDs { // Note that these pods are not valid-- the fake source doesn't // call validation or anything. source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}}) } // Let's wait for the controller to process the things we just added. outputSet := sets.String{} for i := 0; i < len(testIDs); i++ { outputSet.Insert(<-deletionCounter) } for _, key := range outputSet.List() { fmt.Println(key) } // Output: // a-hello // b-controller // c-framework }
// enforcePodContainerConstraints checks for required resources that are not set on this container and // adds them to missingSet. func enforcePodContainerConstraints(container *api.Container, requiredSet, missingSet sets.String) { requests := container.Resources.Requests limits := container.Resources.Limits containerUsage := podUsageHelper(requests, limits) containerSet := quota.ToSet(quota.ResourceNames(containerUsage)) if !containerSet.Equal(requiredSet) { difference := requiredSet.Difference(containerSet) missingSet.Insert(difference.List()...) } }
func NewRequestInfoResolver(c *Config) *apirequest.RequestInfoFactory { apiPrefixes := sets.NewString(strings.Trim(APIGroupPrefix, "/")) // all possible API prefixes legacyAPIPrefixes := sets.String{} // APIPrefixes that won't have groups (legacy) for legacyAPIPrefix := range c.LegacyAPIGroupPrefixes { apiPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/")) legacyAPIPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/")) } return &apirequest.RequestInfoFactory{ APIPrefixes: apiPrefixes, GrouplessAPIPrefixes: legacyAPIPrefixes, } }
// RESTMapper returns a union RESTMapper of all known types with priorities chosen in the following order: // 1. if KUBE_API_VERSIONS is specified, then KUBE_API_VERSIONS in order, OR // 1. legacy kube group preferred version, extensions preferred version, metrics perferred version, legacy // kube any version, extensions any version, metrics any version, all other groups alphabetical preferred version, // all other groups alphabetical. func (m *APIRegistrationManager) RESTMapper(versionPatterns ...schema.GroupVersion) meta.RESTMapper { unionMapper := meta.MultiRESTMapper{} unionedGroups := sets.NewString() for enabledVersion := range m.enabledVersions { if !unionedGroups.Has(enabledVersion.Group) { unionedGroups.Insert(enabledVersion.Group) groupMeta := m.groupMetaMap[enabledVersion.Group] unionMapper = append(unionMapper, groupMeta.RESTMapper) } } if len(versionPatterns) != 0 { resourcePriority := []schema.GroupVersionResource{} kindPriority := []schema.GroupVersionKind{} for _, versionPriority := range versionPatterns { resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource)) kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind)) } return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} } if len(m.envRequestedVersions) != 0 { resourcePriority := []schema.GroupVersionResource{} kindPriority := []schema.GroupVersionKind{} for _, versionPriority := range m.envRequestedVersions { resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource)) kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind)) } return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} } prioritizedGroups := []string{"", "extensions", "metrics"} resourcePriority, kindPriority := m.prioritiesForGroups(prioritizedGroups...) prioritizedGroupsSet := sets.NewString(prioritizedGroups...) remainingGroups := sets.String{} for enabledVersion := range m.enabledVersions { if !prioritizedGroupsSet.Has(enabledVersion.Group) { remainingGroups.Insert(enabledVersion.Group) } } remainingResourcePriority, remainingKindPriority := m.prioritiesForGroups(remainingGroups.List()...) resourcePriority = append(resourcePriority, remainingResourcePriority...) kindPriority = append(kindPriority, remainingKindPriority...) return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority} }
//getWebserverEndpoints returns the webserver endpoints as a set of String, each in the format like "http://{ip}:{port}" func getWebserverEndpoints(client clientset.Interface) sets.String { endpoints, err := client.Core().Endpoints(*namespace).Get(*service, v1.GetOptions{}) eps := sets.String{} if err != nil { state.Logf("Unable to read the endpoints for %v/%v: %v.", *namespace, *service, err) return eps } for _, ss := range endpoints.Subsets { for _, a := range ss.Addresses { for _, p := range ss.Ports { eps.Insert(fmt.Sprintf("http://%s:%d", a.IP, p.Port)) } } } return eps }
func (e *EndpointController) getPodServiceMemberships(pod *v1.Pod) (sets.String, error) { set := sets.String{} services, err := e.serviceStore.GetPodServices(pod) if err != nil { // don't log this error because this function makes pointless // errors when no services match. return set, nil } for i := range services { key, err := keyFunc(services[i]) if err != nil { return nil, err } set.Insert(key) } return set, nil }
func (t *ThirdPartyController) syncResourceList(list runtime.Object) error { existing := sets.String{} switch list := list.(type) { case *extensions.ThirdPartyResourceList: // Loop across all schema objects for third party resources for ix := range list.Items { item := &list.Items[ix] // extract the api group and resource kind from the schema _, group, err := thirdpartyresourcedata.ExtractApiGroupAndKind(item) if err != nil { return err } // place it in the set of resources that we expect, so that we don't delete it in the delete pass existing.Insert(MakeThirdPartyPath(group)) // ensure a RESTful resource for this schema exists on the master if err := t.SyncOneResource(item); err != nil { return err } } default: return fmt.Errorf("expected a *ThirdPartyResourceList, got %#v", list) } // deletion phase, get all installed RESTful resources installed := t.master.ListThirdPartyResources() for _, installedAPI := range installed { found := false // search across the expected restful resources to see if this resource belongs to one of the expected ones for _, apiPath := range existing.List() { if installedAPI == apiPath || strings.HasPrefix(installedAPI, apiPath+"/") { found = true break } } // not expected, delete the resource if !found { if err := t.master.RemoveThirdPartyResource(installedAPI); err != nil { return err } } } return nil }
// cleanupBandwidthLimits updates the status of bandwidth-limited containers // and ensures that only the appropriate CIDRs are active on the node. func (kl *Kubelet) cleanupBandwidthLimits(allPods []*v1.Pod) error { if kl.shaper == nil { return nil } currentCIDRs, err := kl.shaper.GetCIDRs() if err != nil { return err } possibleCIDRs := sets.String{} for ix := range allPods { pod := allPods[ix] ingress, egress, err := bandwidth.ExtractPodBandwidthResources(pod.Annotations) if err != nil { return err } if ingress == nil && egress == nil { glog.V(8).Infof("Not a bandwidth limited container...") continue } status, found := kl.statusManager.GetPodStatus(pod.UID) if !found { // TODO(random-liu): Cleanup status get functions. (issue #20477) s, err := kl.containerRuntime.GetPodStatus(pod.UID, pod.Name, pod.Namespace) if err != nil { return err } status = kl.generateAPIPodStatus(pod, s) } if status.Phase == v1.PodRunning { possibleCIDRs.Insert(fmt.Sprintf("%s/32", status.PodIP)) } } for _, cidr := range currentCIDRs { if !possibleCIDRs.Has(cidr) { glog.V(2).Infof("Removing CIDR: %s (%v)", cidr, possibleCIDRs) if err := kl.shaper.Reset(cidr); err != nil { return err } } } return nil }
func AccumulateUniqueHostPorts(containers []v1.Container, accumulator *sets.String, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} for ci, ctr := range containers { idxPath := fldPath.Index(ci) portsPath := idxPath.Child("ports") for pi := range ctr.Ports { idxPath := portsPath.Index(pi) port := ctr.Ports[pi].HostPort if port == 0 { continue } str := fmt.Sprintf("%d/%s", port, ctr.Ports[pi].Protocol) if accumulator.Has(str) { allErrs = append(allErrs, field.Duplicate(idxPath.Child("hostPort"), str)) } else { accumulator.Insert(str) } } } return allErrs }
func filterInvalidPods(pods []*v1.Pod, source string, recorder record.EventRecorder) (filtered []*v1.Pod) { names := sets.String{} for i, pod := range pods { var errlist field.ErrorList // TODO: remove the conversion when validation is performed on versioned objects. internalPod := &api.Pod{} if err := v1.Convert_v1_Pod_To_api_Pod(pod, internalPod, nil); err != nil { name := kubecontainer.GetPodFullName(pod) glog.Warningf("Pod[%d] (%s) from %s failed to convert to v1, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, v1.EventTypeWarning, "FailedConversion", "Error converting pod %s from %s, ignoring: %v", name, source, err) continue } if errs := validation.ValidatePod(internalPod); len(errs) != 0 { errlist = append(errlist, errs...) // If validation fails, don't trust it any further - // even Name could be bad. } else { name := kubecontainer.GetPodFullName(pod) if names.Has(name) { // TODO: when validation becomes versioned, this gets a bit // more complicated. errlist = append(errlist, field.Duplicate(field.NewPath("metadata", "name"), pod.Name)) } else { names.Insert(name) } } if len(errlist) > 0 { name := bestPodIdentString(pod) err := errlist.ToAggregate() glog.Warningf("Pod[%d] (%s) from %s failed validation, ignoring: %v", i+1, name, source, err) recorder.Eventf(pod, v1.EventTypeWarning, events.FailedValidation, "Error validating pod %s from %s, ignoring: %v", name, source, err) continue } filtered = append(filtered, pod) } return }
func ValidateThirdPartyResource(obj *extensions.ThirdPartyResource) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, false, ValidateThirdPartyResourceName, field.NewPath("metadata"))...) versions := sets.String{} if len(obj.Versions) == 0 { allErrs = append(allErrs, field.Required(field.NewPath("versions"), "must specify at least one version")) } for ix := range obj.Versions { version := &obj.Versions[ix] if len(version.Name) == 0 { allErrs = append(allErrs, field.Invalid(field.NewPath("versions").Index(ix).Child("name"), version, "must not be empty")) } else { for _, msg := range validation.IsDNS1123Label(version.Name) { allErrs = append(allErrs, field.Invalid(field.NewPath("versions").Index(ix).Child("name"), version, msg)) } } if versions.Has(version.Name) { allErrs = append(allErrs, field.Duplicate(field.NewPath("versions").Index(ix).Child("name"), version)) } versions.Insert(version.Name) } return allErrs }
// Object returns a single object representing the output of a single visit to all // found resources. If the Builder was a singular context (expected to return a // single resource by user input) and only a single resource was found, the resource // will be returned as is. Otherwise, the returned resources will be part of an // api.List. The ResourceVersion of the api.List will be set only if it is identical // across all infos returned. func (r *Result) Object() (runtime.Object, error) { infos, err := r.Infos() if err != nil { return nil, err } versions := sets.String{} objects := []runtime.Object{} for _, info := range infos { if info.Object != nil { objects = append(objects, info.Object) versions.Insert(info.ResourceVersion) } } if len(objects) == 1 { if r.singleItemImplied { return objects[0], nil } // if the item is a list already, don't create another list if meta.IsListType(objects[0]) { return objects[0], nil } } version := "" if len(versions) == 1 { version = versions.List()[0] } return &api.List{ ListMeta: metav1.ListMeta{ ResourceVersion: version, }, Items: objects, }, err }
// nodes we observe initially. nodeLabels = make(map[string]string) nodeLabels["kubelet_cleanup"] = "true" nodes := framework.GetReadySchedulableNodesOrDie(c) numNodes = len(nodes.Items) nodeNames = sets.NewString() // If there are a lot of nodes, we don't want to use all of them // (if there are 1000 nodes in the cluster, starting 10 pods/node // will take ~10 minutes today). And there is also deletion phase. // Instead, we choose at most 10 nodes. if numNodes > maxNodesToCheck { numNodes = maxNodesToCheck } for i := 0; i < numNodes; i++ { nodeNames.Insert(nodes.Items[i].Name) } updateNodeLabels(c, nodeNames, nodeLabels, nil) // Start resourceMonitor only in small clusters. if len(nodes.Items) <= maxNodesToCheck { resourceMonitor = framework.NewResourceMonitor(f.ClientSet, framework.TargetContainers(), containerStatsPollingInterval) resourceMonitor.Start() } }) AfterEach(func() { if resourceMonitor != nil { resourceMonitor.Stop() } // If we added labels to nodes in this test, remove them now.
// Test public interface func doTestStore(t *testing.T, store Store) { mkObj := func(id string, val string) testStoreObject { return testStoreObject{id: id, val: val} } store.Add(mkObj("foo", "bar")) if item, ok, _ := store.Get(mkObj("foo", "")); !ok { t.Errorf("didn't find inserted item") } else { if e, a := "bar", item.(testStoreObject).val; e != a { t.Errorf("expected %v, got %v", e, a) } } store.Update(mkObj("foo", "baz")) if item, ok, _ := store.Get(mkObj("foo", "")); !ok { t.Errorf("didn't find inserted item") } else { if e, a := "baz", item.(testStoreObject).val; e != a { t.Errorf("expected %v, got %v", e, a) } } store.Delete(mkObj("foo", "")) if _, ok, _ := store.Get(mkObj("foo", "")); ok { t.Errorf("found deleted item??") } // Test List. store.Add(mkObj("a", "b")) store.Add(mkObj("c", "d")) store.Add(mkObj("e", "e")) { found := sets.String{} for _, item := range store.List() { found.Insert(item.(testStoreObject).val) } if !found.HasAll("b", "d", "e") { t.Errorf("missing items, found: %v", found) } if len(found) != 3 { t.Errorf("extra items") } } // Test Replace. store.Replace([]interface{}{ mkObj("foo", "foo"), mkObj("bar", "bar"), }, "0") { found := sets.String{} for _, item := range store.List() { found.Insert(item.(testStoreObject).val) } if !found.HasAll("foo", "bar") { t.Errorf("missing items") } if len(found) != 2 { t.Errorf("extra items") } } }
func TestSyncAPIs(t *testing.T) { resourcesNamed := func(names ...string) []expapi.ThirdPartyResource { result := []expapi.ThirdPartyResource{} for _, name := range names { result = append(result, expapi.ThirdPartyResource{ObjectMeta: metav1.ObjectMeta{Name: name}}) } return result } tests := []struct { list *expapi.ThirdPartyResourceList apis []string expectedInstalled []string expectedRemoved []string name string }{ { list: &expapi.ThirdPartyResourceList{ Items: resourcesNamed("foo.example.com"), }, expectedInstalled: []string{"foo.example.com"}, name: "simple add", }, { list: &expapi.ThirdPartyResourceList{ Items: resourcesNamed("foo.example.com"), }, apis: []string{ "/apis/example.com", "/apis/example.com/v1", }, name: "does nothing", }, { list: &expapi.ThirdPartyResourceList{ Items: resourcesNamed("foo.example.com"), }, apis: []string{ "/apis/example.com", "/apis/example.com/v1", "/apis/example.co", "/apis/example.co/v1", }, name: "deletes substring API", expectedRemoved: []string{ "/apis/example.co", "/apis/example.co/v1", }, }, { list: &expapi.ThirdPartyResourceList{ Items: resourcesNamed("foo.example.com", "foo.company.com"), }, apis: []string{ "/apis/company.com", "/apis/company.com/v1", }, expectedInstalled: []string{"foo.example.com"}, name: "adds with existing", }, { list: &expapi.ThirdPartyResourceList{ Items: resourcesNamed("foo.example.com"), }, apis: []string{ "/apis/company.com", "/apis/company.com/v1", }, expectedInstalled: []string{"foo.example.com"}, expectedRemoved: []string{"/apis/company.com", "/apis/company.com/v1"}, name: "removes with existing", }, } for _, test := range tests { fake := FakeAPIInterface{ apis: test.apis, t: t, } cntrl := ThirdPartyController{master: &fake} if err := cntrl.syncResourceList(test.list); err != nil { t.Errorf("[%s] unexpected error: %v", test.name, err) } if len(test.expectedInstalled) != len(fake.installed) { t.Errorf("[%s] unexpected installed APIs: %d, expected %d (%#v)", test.name, len(fake.installed), len(test.expectedInstalled), fake.installed[0]) continue } else { names := sets.String{} for ix := range fake.installed { names.Insert(fake.installed[ix].Name) } for _, name := range test.expectedInstalled { if !names.Has(name) { t.Errorf("[%s] missing installed API: %s", test.name, name) } } } if len(test.expectedRemoved) != len(fake.removed) { t.Errorf("[%s] unexpected installed APIs: %d, expected %d", test.name, len(fake.removed), len(test.expectedRemoved)) continue } else { names := sets.String{} names.Insert(fake.removed...) for _, name := range test.expectedRemoved { if !names.Has(name) { t.Errorf("[%s] missing removed API: %s (%s)", test.name, name, names) } } } } }
func TestWatchCacheBasic(t *testing.T) { store := newTestWatchCache(2) // Test Add/Update/Delete. pod1 := makeTestPod("pod", 1) if err := store.Add(pod1); err != nil { t.Errorf("unexpected error: %v", err) } if item, ok, _ := store.Get(pod1); !ok { t.Errorf("didn't find pod") } else { if !api.Semantic.DeepEqual(&storeElement{Key: "prefix/ns/pod", Object: pod1}, item) { t.Errorf("expected %v, got %v", pod1, item) } } pod2 := makeTestPod("pod", 2) if err := store.Update(pod2); err != nil { t.Errorf("unexpected error: %v", err) } if item, ok, _ := store.Get(pod2); !ok { t.Errorf("didn't find pod") } else { if !api.Semantic.DeepEqual(&storeElement{Key: "prefix/ns/pod", Object: pod2}, item) { t.Errorf("expected %v, got %v", pod1, item) } } pod3 := makeTestPod("pod", 3) if err := store.Delete(pod3); err != nil { t.Errorf("unexpected error: %v", err) } if _, ok, _ := store.Get(pod3); ok { t.Errorf("found pod") } // Test List. store.Add(makeTestPod("pod1", 4)) store.Add(makeTestPod("pod2", 5)) store.Add(makeTestPod("pod3", 6)) { podNames := sets.String{} for _, item := range store.List() { podNames.Insert(item.(*storeElement).Object.(*api.Pod).ObjectMeta.Name) } if !podNames.HasAll("pod1", "pod2", "pod3") { t.Errorf("missing pods, found %v", podNames) } if len(podNames) != 3 { t.Errorf("found missing/extra items") } } // Test Replace. store.Replace([]interface{}{ makeTestPod("pod4", 7), makeTestPod("pod5", 8), }, "8") { podNames := sets.String{} for _, item := range store.List() { podNames.Insert(item.(*storeElement).Object.(*api.Pod).ObjectMeta.Name) } if !podNames.HasAll("pod4", "pod5") { t.Errorf("missing pods, found %v", podNames) } if len(podNames) != 2 { t.Errorf("found missing/extra items") } } }
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 := fcache.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 := 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 := NewInformer( source, &v1.Pod{}, time.Millisecond*100, 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 wait.Poll(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { return controller.HasSynced(), nil }) 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 := &v1.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. // TODO: look in the queue to see how many items need to be processed. time.Sleep(100 * time.Millisecond) close(stop) // TODO: Verify that no goroutines were leaked here and that everything shut // down cleanly. outputSetLock.Lock() t.Logf("got: %#v", outputSet) }
func Example() { // source simulates an apiserver object endpoint. source := fcache.NewFakeControllerSource() // This will hold the downstream state, as we know it. downstream := NewStore(DeletionHandlingMetaNamespaceKeyFunc) // This will hold incoming changes. Note how we pass downstream in as a // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, nil, downstream) // Let's do threadsafe output to get predictable test results. deletionCounter := make(chan string, 1000) cfg := &Config{ Queue: fifo, ListerWatcher: source, ObjectType: &v1.Pod{}, FullResyncPeriod: time.Millisecond * 100, RetryOnError: false, // Let's implement a simple controller that just deletes // everything that comes in. Process: func(obj interface{}) error { // Obj is from the Pop method of the Queue we make above. newest := obj.(Deltas).Newest() if newest.Type != Deleted { // Update our downstream store. err := downstream.Add(newest.Object) if err != nil { return err } // Delete this object. source.Delete(newest.Object.(runtime.Object)) } else { // Update our downstream store. err := downstream.Delete(newest.Object) if err != nil { return err } // fifo's KeyOf is easiest, because it handles // DeletedFinalStateUnknown markers. key, err := fifo.KeyOf(newest.Object) if err != nil { return err } // Report this deletion. deletionCounter <- key } return nil }, } // Create the controller and run it until we close stop. stop := make(chan struct{}) defer close(stop) go New(cfg).Run(stop) // Let's add a few objects to the source. testIDs := []string{"a-hello", "b-controller", "c-framework"} for _, name := range testIDs { // Note that these pods are not valid-- the fake source doesn't // call validation or anything. source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}}) } // Let's wait for the controller to process the things we just added. outputSet := sets.String{} for i := 0; i < len(testIDs); i++ { outputSet.Insert(<-deletionCounter) } for _, key := range outputSet.List() { fmt.Println(key) } // Output: // a-hello // b-controller // c-framework }
// ipamGarbageCollection will release unused IP. // kubenet uses the CNI bridge plugin, which stores allocated ips on file. Each // file created under defaultIPAMDir has the format: ip/container-hash. So this // routine looks for hashes that are not reported by the currently running docker, // and invokes DelNetwork on each one. Note that this will only work for the // current CNI bridge plugin, because we have no way of finding the NetNs. func (plugin *kubenetNetworkPlugin) ipamGarbageCollection() { glog.V(2).Infof("Starting IP garbage collection") ipamDir := filepath.Join(defaultIPAMDir, KubenetPluginName) files, err := ioutil.ReadDir(ipamDir) if err != nil { glog.Errorf("Failed to list files in %q: %v", ipamDir, err) return } // gather containerIDs for allocated ips ipContainerIdMap := make(map[string]string) for _, file := range files { // skip non checkpoint file if ip := net.ParseIP(file.Name()); ip == nil { continue } content, err := ioutil.ReadFile(filepath.Join(ipamDir, file.Name())) if err != nil { glog.Errorf("Failed to read file %v: %v", file, err) } ipContainerIdMap[file.Name()] = strings.TrimSpace(string(content)) } // gather infra container IDs of current running Pods runningContainerIDs := utilsets.String{} pods, err := plugin.getNonExitedPods() if err != nil { glog.Errorf("Failed to get pods: %v", err) return } for _, pod := range pods { containerID, err := plugin.host.GetRuntime().GetPodContainerID(pod) if err != nil { glog.Warningf("Failed to get infra containerID of %q/%q: %v", pod.Namespace, pod.Name, err) continue } runningContainerIDs.Insert(strings.TrimSpace(containerID.ID)) } // release leaked ips for ip, containerID := range ipContainerIdMap { // if the container is not running, release IP if runningContainerIDs.Has(containerID) { continue } // CNI requires all config to be presented, although only containerID is needed in this case rt := &libcni.RuntimeConf{ ContainerID: containerID, IfName: network.DefaultInterfaceName, // TODO: How do we find the NetNs of an exited container? docker inspect // doesn't show us the pid, so we probably need to checkpoint NetNS: "", } glog.V(2).Infof("Releasing IP %q allocated to %q.", ip, containerID) // CNI bridge plugin should try to release IP and then return if err := plugin.cniConfig.DelNetwork(plugin.netConfig, rt); err != nil { glog.Errorf("Error while releasing IP: %v", err) } } }
var nodeNames sets.String f := framework.NewDefaultFramework("kubelet-perf") var om *framework.RuntimeOperationMonitor var rm *framework.ResourceMonitor BeforeEach(func() { // Wait until image prepull pod has completed so that they wouldn't // affect the runtime cpu usage. Fail the test if prepulling cannot // finish in time. if err := framework.WaitForPodsSuccess(f.ClientSet, api.NamespaceSystem, framework.ImagePullerLabels, imagePrePullingLongTimeout); err != nil { framework.Failf("Image puller didn't complete in %v, not running resource usage test since the metrics might be adultrated", imagePrePullingLongTimeout) } nodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet) nodeNames = sets.NewString() for _, node := range nodes.Items { nodeNames.Insert(node.Name) } om = framework.NewRuntimeOperationMonitor(f.ClientSet) rm = framework.NewResourceMonitor(f.ClientSet, framework.TargetContainers(), containerStatsPollingPeriod) rm.Start() }) AfterEach(func() { rm.Stop() result := om.GetLatestRuntimeOperationErrorRate() framework.Logf("runtime operation error metrics:\n%s", framework.FormatRuntimeOperationErrorRate(result)) }) framework.KubeDescribe("regular resource usage tracking", func() { // We assume that the scheduler will make reasonable scheduling choices // and assign ~N pods on the node. // Although we want to track N pods per node, there are N + add-on pods
func TestList(t *testing.T) { server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix()) defer server.Terminate(t) cacher := newTestCacher(etcdStorage, 10) defer cacher.Stop() podFoo := makeTestPod("foo") podBar := makeTestPod("bar") podBaz := makeTestPod("baz") podFooPrime := makeTestPod("foo") podFooPrime.Spec.NodeName = "fakeNode" fooCreated := updatePod(t, etcdStorage, podFoo, nil) _ = updatePod(t, etcdStorage, podBar, nil) _ = updatePod(t, etcdStorage, podBaz, nil) _ = updatePod(t, etcdStorage, podFooPrime, fooCreated) // Create a pod in a namespace that contains "ns" as a prefix // Make sure it is not returned in a watch of "ns" podFooNS2 := makeTestPod("foo") podFooNS2.Namespace += "2" updatePod(t, etcdStorage, podFooNS2, nil) deleted := api.Pod{} if err := etcdStorage.Delete(context.TODO(), "pods/ns/bar", &deleted, nil); err != nil { t.Errorf("Unexpected error: %v", err) } // We first List directly from etcd by passing empty resourceVersion, // to get the current etcd resourceVersion. rvResult := &api.PodList{} if err := cacher.List(context.TODO(), "pods/ns", "", storage.Everything, rvResult); err != nil { t.Errorf("Unexpected error: %v", err) } deletedPodRV := rvResult.ListMeta.ResourceVersion result := &api.PodList{} // We pass the current etcd ResourceVersion received from the above List() operation, // since there is not easy way to get ResourceVersion of barPod deletion operation. if err := cacher.List(context.TODO(), "pods/ns", deletedPodRV, storage.Everything, result); err != nil { t.Errorf("Unexpected error: %v", err) } if result.ListMeta.ResourceVersion != deletedPodRV { t.Errorf("Incorrect resource version: %v", result.ListMeta.ResourceVersion) } if len(result.Items) != 2 { t.Errorf("Unexpected list result: %d", len(result.Items)) } keys := sets.String{} for _, item := range result.Items { keys.Insert(item.Name) } if !keys.HasAll("foo", "baz") { t.Errorf("Unexpected list result: %#v", result) } for _, item := range result.Items { // unset fields that are set by the infrastructure item.ResourceVersion = "" item.CreationTimestamp = metav1.Time{} if item.Namespace != "ns" { t.Errorf("Unexpected namespace: %s", item.Namespace) } var expected *api.Pod switch item.Name { case "foo": expected = podFooPrime case "baz": expected = podBaz default: t.Errorf("Unexpected item: %v", item) } if e, a := *expected, item; !reflect.DeepEqual(e, a) { t.Errorf("Expected: %#v, got: %#v", e, a) } } }