func parseMesosState(blob []byte) (*mesosState, error) { type State struct { ClusterName string `json:"cluster"` Slaves []*struct { Id string `json:"id"` // ex: 20150106-162714-3815890698-5050-2453-S2 Pid string `json:"pid"` // ex: slave(1)@10.22.211.18:5051 Hostname string `json:"hostname"` // ex: 10.22.211.18, or slave-123.nowhere.com Resources map[string]interface{} `json:"resources"` // ex: {"mem": 123, "ports": "[31000-3200]"} } `json:"slaves"` } state := &State{ClusterName: defaultClusterName} if err := json.Unmarshal(blob, state); err != nil { return nil, err } nodes := []*slaveNode{} for _, slave := range state.Slaves { if slave.Hostname == "" { continue } node := &slaveNode{hostname: slave.Hostname} cap := api.ResourceList{} if slave.Resources != nil && len(slave.Resources) > 0 { // attempt to translate CPU (cores) and memory (MB) resources if cpu, found := slave.Resources["cpus"]; found { if cpuNum, ok := cpu.(float64); ok { cap[api.ResourceCPU] = *resource.NewQuantity(int64(cpuNum), resource.DecimalSI) } else { log.Warningf("unexpected slave cpu resource type %T: %v", cpu, cpu) } } else { log.Warningf("slave failed to report cpu resource") } if mem, found := slave.Resources["mem"]; found { if memNum, ok := mem.(float64); ok { cap[api.ResourceMemory] = *resource.NewQuantity(int64(memNum), resource.BinarySI) } else { log.Warningf("unexpected slave mem resource type %T: %v", mem, mem) } } else { log.Warningf("slave failed to report mem resource") } } if len(cap) > 0 { node.resources = &api.NodeResources{ Capacity: cap, } log.V(4).Infof("node %q reporting capacity %v", node.hostname, cap) } nodes = append(nodes, node) } result := &mesosState{ clusterName: state.ClusterName, nodes: nodes, } return result, nil }
func makeResources(milliCPU int64, memory int64, pods int64) api.NodeResources { return api.NodeResources{ Capacity: api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI), api.ResourcePods: *resource.NewQuantity(pods, resource.DecimalSI), }, } }
func ExampleFormat() { memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI) fmt.Printf("memorySize = %v\n", memorySize) diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI) fmt.Printf("diskSize = %v\n", diskSize) cores := resource.NewMilliQuantity(5300, resource.DecimalSI) fmt.Printf("cores = %v\n", cores) // Output: // memorySize = 5Gi // diskSize = 5G // cores = 5300m }
// PodMemory computes total memory limit across all containers in a pod // TODO: Remove this once the mesos scheduler becomes request aware func PodMemory(pod *api.Pod) *resource.Quantity { val := int64(0) for j := range pod.Spec.Containers { val = val + pod.Spec.Containers[j].Resources.Limits.Memory().Value() } return resource.NewQuantity(int64(val), resource.DecimalSI) }
// Instances returns an implementation of Instances for OpenStack. func (os *OpenStack) Instances() (cloudprovider.Instances, bool) { glog.V(4).Info("openstack.Instances() called") compute, err := openstack.NewComputeV2(os.provider, gophercloud.EndpointOpts{ Region: os.region, }) if err != nil { glog.Warningf("Failed to find compute endpoint: %v", err) return nil, false } pager := flavors.ListDetail(compute, nil) flavor_to_resource := make(map[string]*api.NodeResources) err = pager.EachPage(func(page pagination.Page) (bool, error) { flavorList, err := flavors.ExtractFlavors(page) if err != nil { return false, err } for _, flavor := range flavorList { rsrc := api.NodeResources{ Capacity: api.ResourceList{ api.ResourceCPU: *resource.NewQuantity(int64(flavor.VCPUs), resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(int64(flavor.RAM)*MiB, resource.BinarySI), "openstack.org/disk": *resource.NewQuantity(int64(flavor.Disk)*GB, resource.DecimalSI), "openstack.org/rxTxFactor": *resource.NewMilliQuantity(int64(flavor.RxTxFactor)*1000, resource.DecimalSI), "openstack.org/swap": *resource.NewQuantity(int64(flavor.Swap)*MiB, resource.BinarySI), }, } flavor_to_resource[flavor.ID] = &rsrc } return true, nil }) if err != nil { glog.Warningf("Failed to find compute flavors: %v", err) return nil, false } glog.V(3).Infof("Found %v compute flavors", len(flavor_to_resource)) glog.V(1).Info("Claiming to support Instances") return &Instances{compute, flavor_to_resource}, true }
func TestLimitedResources(t *testing.T) { assert := assert.New(t) task, _ := fakePodTask("limited") pod := &task.Pod pod.Spec = api.PodSpec{ Containers: []api.Container{{ Name: "a", Resources: api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceCPU: *resource.NewQuantity(1, resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(256*1024*1024, resource.BinarySI), }, }, }, { Name: "b", Resources: api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(512*1024*1024, resource.BinarySI), }, }, }}, } beforeLimitingCPU := mresource.CPUForPod(pod, mresource.DefaultDefaultContainerCPULimit) beforeLimitingMem := mresource.MemForPod(pod, mresource.DefaultDefaultContainerMemLimit) unboundedCPU := mresource.LimitPodCPU(pod, mresource.DefaultDefaultContainerCPULimit) unboundedMem := mresource.LimitPodMem(pod, mresource.DefaultDefaultContainerMemLimit) cpu := mresource.PodCPULimit(pod) mem := mresource.PodMemLimit(pod) assert.False(unboundedCPU, "CPU resources are defined as limited") assert.False(unboundedMem, "mem resources are defined as limited") assert.Equal(3.0, float64(cpu)) assert.Equal(768.0, float64(mem)) assert.Equal(cpu, beforeLimitingCPU) assert.Equal(mem, beforeLimitingMem) }
func makeNode(node string, milliCPU, memory int64) api.Node { return api.Node{ ObjectMeta: api.ObjectMeta{Name: node}, Status: api.NodeStatus{ Capacity: api.ResourceList{ "cpu": *resource.NewMilliQuantity(milliCPU, resource.DecimalSI), "memory": *resource.NewQuantity(memory, resource.BinarySI), }, }, } }
func CapacityFromMachineInfo(info *cadvisorApi.MachineInfo) api.ResourceList { c := api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity( int64(info.NumCores*1000), resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity( info.MemoryCapacity, resource.BinarySI), } return c }
func newResourcePod(usage ...resourceRequest) *api.Pod { containers := []api.Container{} for _, req := range usage { containers = append(containers, api.Container{ Resources: api.ResourceRequirements{ Requests: api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity(req.milliCPU, resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(req.memory, resource.BinarySI), }, }, }) } return &api.Pod{ Spec: api.PodSpec{ Containers: containers, }, } }
func TestCreater(t *testing.T) { tempPath := "/tmp/hostpath/" defer os.RemoveAll(tempPath) err := os.MkdirAll(tempPath, 0750) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost("/tmp/fake", nil, nil)) spec := &volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{HostPath: &api.HostPathVolumeSource{Path: tempPath}}}}} plug, err := plugMgr.FindCreatablePluginBySpec(spec) if err != nil { t.Errorf("Can't find the plugin by name") } creater, err := plug.NewCreater(volume.VolumeOptions{CapacityMB: 100, PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete}) if err != nil { t.Errorf("Failed to make a new Creater: %v", err) } pv, err := creater.Create() if err != nil { t.Errorf("Unexpected error creating volume: %v", err) } if pv.Spec.HostPath.Path == "" { t.Errorf("Expected pv.Spec.HostPath.Path to not be empty: %#v", pv) } expectedCapacity := resource.NewQuantity(100*1024*1024, resource.BinarySI) actualCapacity := pv.Spec.Capacity[api.ResourceStorage] expectedAmt := expectedCapacity.Value() actualAmt := actualCapacity.Value() if expectedAmt != actualAmt { t.Errorf("Expected capacity %+v but got %+v", expectedAmt, actualAmt) } if pv.Spec.PersistentVolumeReclaimPolicy != api.PersistentVolumeReclaimDelete { t.Errorf("Expected reclaim policy %+v but got %+v", api.PersistentVolumeReclaimDelete, pv.Spec.PersistentVolumeReclaimPolicy) } os.RemoveAll(pv.Spec.HostPath.Path) }
// sum takes the total of each named resource across all inputs // if a key is not in each input, then the output resource list will omit the key func sum(inputs []api.ResourceList) api.ResourceList { result := api.ResourceList{} keys := []api.ResourceName{} for i := range inputs { for k := range inputs[i] { keys = append(keys, k) } } for _, key := range keys { total, isSet := int64(0), true for i := range inputs { input := inputs[i] v, exists := input[key] if exists { if key == api.ResourceCPU { total = total + v.MilliValue() } else { total = total + v.Value() } } else { isSet = false } } if isSet { if key == api.ResourceCPU { result[key] = *(resource.NewMilliQuantity(total, resource.DecimalSI)) } else { result[key] = *(resource.NewQuantity(total, resource.DecimalSI)) } } } return result }
// IncrementUsage updates the supplied ResourceQuotaStatus object based on the incoming operation // Return true if the usage must be recorded prior to admitting the new resource // Return an error if the operation should not pass admission control func IncrementUsage(a admission.Attributes, status *api.ResourceQuotaStatus, client client.Interface) (bool, error) { var errs []error dirty := true set := map[api.ResourceName]bool{} for k := range status.Hard { set[k] = true } obj := a.GetObject() // handle max counts for each kind of resource (pods, services, replicationControllers, etc.) if a.GetOperation() == admission.Create { resourceName := resourceToResourceName[a.GetResource()] hard, hardFound := status.Hard[resourceName] if hardFound { used, usedFound := status.Used[resourceName] if !usedFound { return false, fmt.Errorf("quota usage stats are not yet known, unable to admit resource until an accurate count is completed.") } if used.Value() >= hard.Value() { errs = append(errs, fmt.Errorf("limited to %s %s", hard.String(), resourceName)) dirty = false } else { status.Used[resourceName] = *resource.NewQuantity(used.Value()+int64(1), resource.DecimalSI) } } } if a.GetResource() == "pods" { for _, resourceName := range []api.ResourceName{api.ResourceMemory, api.ResourceCPU} { // ignore tracking the resource if it's not in the quota document if !set[resourceName] { continue } hard, hardFound := status.Hard[resourceName] if !hardFound { continue } // if we do not yet know how much of the current resource is used, we cannot accept any request used, usedFound := status.Used[resourceName] if !usedFound { return false, fmt.Errorf("unable to admit pod until quota usage stats are calculated.") } // the amount of resource being requested, or an error if it does not make a request that is tracked pod := obj.(*api.Pod) delta, err := resourcequotacontroller.PodRequests(pod, resourceName) if err != nil { return false, fmt.Errorf("must make a non-zero request for %s since it is tracked by quota.", resourceName) } // if this operation is an update, we need to find the delta usage from the previous state if a.GetOperation() == admission.Update { oldPod, err := client.Pods(a.GetNamespace()).Get(pod.Name) if err != nil { return false, err } // if the previous version of the resource made a resource request, we need to subtract the old request // from the current to get the actual resource request delta. if the previous version of the pod // made no request on the resource, then we get an err value. we ignore the err value, and delta // will just be equal to the total resource request on the pod since there is nothing to subtract. oldRequest, err := resourcequotacontroller.PodRequests(oldPod, resourceName) if err == nil { err = delta.Sub(*oldRequest) if err != nil { return false, err } } } newUsage := used.Copy() newUsage.Add(*delta) // make the most precise comparison possible newUsageValue := newUsage.Value() hardUsageValue := hard.Value() if newUsageValue <= resource.MaxMilliValue && hardUsageValue <= resource.MaxMilliValue { newUsageValue = newUsage.MilliValue() hardUsageValue = hard.MilliValue() } if newUsageValue > hardUsageValue { errs = append(errs, fmt.Errorf("unable to admit pod without exceeding quota for resource %s: limited to %s but require %s to succeed.", resourceName, hard.String(), newUsage.String())) dirty = false } else { status.Used[resourceName] = *newUsage } } } return dirty, errors.NewAggregate(errs) }
// syncResourceQuota runs a complete sync of current status func (rm *ResourceQuotaController) syncResourceQuota(quota api.ResourceQuota) (err error) { // quota is dirty if any part of spec hard limits differs from the status hard limits dirty := !api.Semantic.DeepEqual(quota.Spec.Hard, quota.Status.Hard) // dirty tracks if the usage status differs from the previous sync, // if so, we send a new usage with latest status // if this is our first sync, it will be dirty by default, since we need track usage dirty = dirty || (quota.Status.Hard == nil || quota.Status.Used == nil) // Create a usage object that is based on the quota resource version usage := api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: quota.Name, Namespace: quota.Namespace, ResourceVersion: quota.ResourceVersion, Labels: quota.Labels, Annotations: quota.Annotations}, Status: api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, }, } // set the hard values supported on the quota for k, v := range quota.Spec.Hard { usage.Status.Hard[k] = *v.Copy() } // set any last known observed status values for usage for k, v := range quota.Status.Used { usage.Status.Used[k] = *v.Copy() } set := map[api.ResourceName]bool{} for k := range usage.Status.Hard { set[k] = true } pods := &api.PodList{} if set[api.ResourcePods] || set[api.ResourceMemory] || set[api.ResourceCPU] { pods, err = rm.kubeClient.Pods(usage.Namespace).List(labels.Everything(), fields.Everything()) if err != nil { return err } } filteredPods := FilterQuotaPods(pods.Items) // iterate over each resource, and update observation for k := range usage.Status.Hard { // look if there is a used value, if none, we are definitely dirty prevQuantity, found := usage.Status.Used[k] if !found { dirty = true } var value *resource.Quantity switch k { case api.ResourcePods: value = resource.NewQuantity(int64(len(filteredPods)), resource.DecimalSI) case api.ResourceServices: items, err := rm.kubeClient.Services(usage.Namespace).List(labels.Everything()) if err != nil { return err } value = resource.NewQuantity(int64(len(items.Items)), resource.DecimalSI) case api.ResourceReplicationControllers: items, err := rm.kubeClient.ReplicationControllers(usage.Namespace).List(labels.Everything()) if err != nil { return err } value = resource.NewQuantity(int64(len(items.Items)), resource.DecimalSI) case api.ResourceQuotas: items, err := rm.kubeClient.ResourceQuotas(usage.Namespace).List(labels.Everything()) if err != nil { return err } value = resource.NewQuantity(int64(len(items.Items)), resource.DecimalSI) case api.ResourceSecrets: items, err := rm.kubeClient.Secrets(usage.Namespace).List(labels.Everything(), fields.Everything()) if err != nil { return err } value = resource.NewQuantity(int64(len(items.Items)), resource.DecimalSI) case api.ResourcePersistentVolumeClaims: items, err := rm.kubeClient.PersistentVolumeClaims(usage.Namespace).List(labels.Everything(), fields.Everything()) if err != nil { return err } value = resource.NewQuantity(int64(len(items.Items)), resource.DecimalSI) case api.ResourceMemory: value = PodsRequests(filteredPods, api.ResourceMemory) case api.ResourceCPU: value = PodsRequests(filteredPods, api.ResourceCPU) } // ignore fields we do not understand (assume another controller is tracking it) if value != nil { // see if the value has changed dirty = dirty || (value.Value() != prevQuantity.Value()) // just update the value usage.Status.Used[k] = *value } } // update the usage only if it changed if dirty { _, err = rm.kubeClient.ResourceQuotas(usage.Namespace).UpdateStatus(&usage) return err } return nil }
sum, count, timestamp := calculateSumFromLatestSample(metrics) value := "0" if count > 0 { // assumes that cpu usage is in millis value = fmt.Sprintf("%dm", sum/uint64(count)) } return ResourceConsumption{Resource: api.ResourceCPU, Quantity: resource.MustParse(value)}, count, timestamp }}, api.ResourceMemory: {"memory-usage", func(metrics heapster.MetricResultList) (ResourceConsumption, int, time.Time) { sum, count, timestamp := calculateSumFromLatestSample(metrics) value := int64(0) if count > 0 { value = int64(sum) / int64(count) } return ResourceConsumption{Resource: api.ResourceMemory, Quantity: *resource.NewQuantity(value, resource.DecimalSI)}, count, timestamp }}, } // NewHeapsterMetricsClient returns a new instance of Heapster-based implementation of MetricsClient interface. func NewHeapsterMetricsClient(client client.Interface) *HeapsterMetricsClient { return &HeapsterMetricsClient{ client: client, resourceDefinitions: heapsterMetricDefinitions, } } func (h *HeapsterMetricsClient) GetCPUUtilization(namespace string, selector map[string]string) (*int, time.Time, error) { consumption, request, timestamp, err := h.GetResourceConsumptionAndRequest(api.ResourceCPU, namespace, selector) if err != nil { return nil, time.Time{}, fmt.Errorf("failed to get CPU consumption and request: %v", err)
func TestLimit(t *testing.T) { tests := []struct { cidr string ingress *resource.Quantity egress *resource.Quantity expectErr bool expectedCalls int err error }{ { cidr: "1.2.3.4/32", ingress: resource.NewQuantity(10, resource.DecimalSI), egress: resource.NewQuantity(20, resource.DecimalSI), expectedCalls: 6, }, { cidr: "1.2.3.4/32", ingress: resource.NewQuantity(10, resource.DecimalSI), egress: nil, expectedCalls: 3, }, { cidr: "1.2.3.4/32", ingress: nil, egress: resource.NewQuantity(20, resource.DecimalSI), expectedCalls: 3, }, { cidr: "1.2.3.4/32", ingress: nil, egress: nil, expectedCalls: 0, }, { err: errors.New("test error"), ingress: resource.NewQuantity(10, resource.DecimalSI), egress: resource.NewQuantity(20, resource.DecimalSI), expectErr: true, }, } for _, test := range tests { fcmd := exec.FakeCmd{ CombinedOutputScript: []exec.FakeCombinedOutputAction{ func() ([]byte, error) { return []byte(tcClassOutput), test.err }, func() ([]byte, error) { return []byte{}, test.err }, func() ([]byte, error) { return []byte{}, test.err }, func() ([]byte, error) { return []byte(tcClassOutput2), test.err }, func() ([]byte, error) { return []byte{}, test.err }, func() ([]byte, error) { return []byte{}, test.err }, }, } fexec := exec.FakeExec{ CommandScript: []exec.FakeCommandAction{ func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } iface := "cbr0" shaper := &tcShaper{e: &fexec, iface: iface} if err := shaper.Limit(test.cidr, test.ingress, test.egress); err != nil && !test.expectErr { t.Errorf("unexpected error: %v", err) return } else if err == nil && test.expectErr { t.Error("unexpected non-error") return } // No more testing in the error case if test.expectErr { if fcmd.CombinedOutputCalls != 1 { t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls) } return } if fcmd.CombinedOutputCalls != test.expectedCalls { t.Errorf("unexpected number of calls: %d, expected: %d", fcmd.CombinedOutputCalls, test.expectedCalls) } for ix := range fcmd.CombinedOutputLog { output := fcmd.CombinedOutputLog[ix] if output[0] != "tc" { t.Errorf("unexpected command: %s, expected tc", output[0]) } if output[4] != iface { t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output) } if ix == 1 { var expectedRate string if test.ingress != nil { expectedRate = makeKBitString(test.ingress) } else { expectedRate = makeKBitString(test.egress) } if output[11] != expectedRate { t.Errorf("unexpected ingress: %s, expected: %s", output[11], expectedRate) } if output[8] != "1:5" { t.Errorf("unexpected class: %s, expected: %s", output[8], "1:5") } } if ix == 2 { if output[15] != test.cidr { t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr) } if output[17] != "1:5" { t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5") } } if ix == 4 { if output[11] != makeKBitString(test.egress) { t.Errorf("unexpected egress: %s, expected: %s", output[11], makeKBitString(test.egress)) } if output[8] != "1:6" { t.Errorf("unexpected class: %s, expected: %s", output[8], "1:6") } } if ix == 5 { if output[15] != test.cidr { t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr) } if output[17] != "1:6" { t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5") } } } } }
func testPDPod(diskNames []string, targetHost string, readOnly bool, numContainers int) *api.Pod { containers := make([]api.Container, numContainers) for i := range containers { containers[i].Name = "mycontainer" if numContainers > 1 { containers[i].Name = fmt.Sprintf("mycontainer%v", i+1) } containers[i].Image = "gcr.io/google_containers/busybox" containers[i].Command = []string{"sleep", "6000"} containers[i].VolumeMounts = make([]api.VolumeMount, len(diskNames)) for k := range diskNames { containers[i].VolumeMounts[k].Name = fmt.Sprintf("testpd%v", k+1) containers[i].VolumeMounts[k].MountPath = fmt.Sprintf("/testpd%v", k+1) } containers[i].Resources.Limits = api.ResourceList{} containers[i].Resources.Limits[api.ResourceCPU] = *resource.NewQuantity(int64(0), resource.DecimalSI) } pod := &api.Pod{ TypeMeta: unversioned.TypeMeta{ Kind: "Pod", APIVersion: latest.GroupOrDie("").Version, }, ObjectMeta: api.ObjectMeta{ Name: "pd-test-" + string(util.NewUUID()), }, Spec: api.PodSpec{ Containers: containers, NodeName: targetHost, }, } if testContext.Provider == "gce" || testContext.Provider == "gke" { pod.Spec.Volumes = make([]api.Volume, len(diskNames)) for k, diskName := range diskNames { pod.Spec.Volumes[k].Name = fmt.Sprintf("testpd%v", k+1) pod.Spec.Volumes[k].VolumeSource = api.VolumeSource{ GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{ PDName: diskName, FSType: "ext4", ReadOnly: readOnly, }, } } } else if testContext.Provider == "aws" { pod.Spec.Volumes = make([]api.Volume, len(diskNames)) for k, diskName := range diskNames { pod.Spec.Volumes[k].Name = fmt.Sprintf("testpd%v", k+1) pod.Spec.Volumes[k].VolumeSource = api.VolumeSource{ AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{ VolumeID: diskName, FSType: "ext4", ReadOnly: readOnly, }, } } } else { panic("Unknown provider: " + testContext.Provider) } return pod }
// limitPodMem sets DefaultContainerMem for the memory limit of each container that // does not limit its memory resource yet. limitPodMem returns true if and only if // at least one container had no memory limit set. func LimitPodMem(pod *api.Pod, defaultLimit MegaBytes) bool { defaultMemQuantity := resource.NewQuantity(int64(float64(defaultLimit)*1024.0*1024.0), resource.BinarySI) return limitPodResource(pod, api.ResourceMemory, *defaultMemQuantity) }
// FuzzerFor can randomly populate api objects that are destined for version. func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { f := fuzz.New().NilChance(.5).NumElements(1, 1) if src != nil { f.RandSource(src) } f.Funcs( func(j *runtime.PluginBase, c fuzz.Continue) { // Do nothing; this struct has only a Kind field and it must stay blank in memory. }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *unversioned.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = "" j.Kind = "" }, func(j *api.ObjectMeta, c fuzz.Continue) { j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() j.UID = types.UID(c.RandString()) j.GenerateName = c.RandString() var sec, nsec int64 c.Fuzz(&sec) c.Fuzz(&nsec) j.CreationTimestamp = unversioned.Unix(sec, nsec).Rfc3339Copy() }, func(j *api.ObjectReference, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. j.APIVersion = c.RandString() j.Kind = c.RandString() j.Namespace = c.RandString() j.Name = c.RandString() j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.FieldPath = c.RandString() }, func(j *unversioned.ListMeta, c fuzz.Continue) { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, func(j *api.ListOptions, c fuzz.Continue) { // TODO: add some parsing j.LabelSelector, _ = labels.Parse("a=b") j.FieldSelector, _ = fields.ParseSelector("a=b") }, func(s *api.PodSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // has a default value ttl := int64(30) if c.RandBool() { ttl = int64(c.Uint32()) } s.TerminationGracePeriodSeconds = &ttl c.Fuzz(s.SecurityContext) if s.SecurityContext == nil { s.SecurityContext = new(api.PodSecurityContext) } }, func(j *api.PodPhase, c fuzz.Continue) { statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} *j = statuses[c.Rand.Intn(len(statuses))] }, func(j *api.PodTemplateSpec, c fuzz.Continue) { // TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2 // conversion compare converted object to nil via DeepEqual j.ObjectMeta = api.ObjectMeta{} c.Fuzz(&j.ObjectMeta) j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels} j.Spec = api.PodSpec{} c.Fuzz(&j.Spec) }, func(j *api.Binding, c fuzz.Continue) { c.Fuzz(&j.ObjectMeta) j.Target.Name = c.RandString() }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again //j.TemplateRef = nil // this is required for round trip }, func(j *extensions.DeploymentStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // Ensure that strategyType is one of valid values. strategyTypes := []extensions.DeploymentStrategyType{extensions.RecreateDeploymentStrategyType, extensions.RollingUpdateDeploymentStrategyType} j.Type = strategyTypes[c.Rand.Intn(len(strategyTypes))] if j.Type != extensions.RollingUpdateDeploymentStrategyType { j.RollingUpdate = nil } else { rollingUpdate := extensions.RollingUpdateDeployment{} if c.RandBool() { rollingUpdate.MaxUnavailable = util.NewIntOrStringFromInt(int(c.RandUint64())) rollingUpdate.MaxSurge = util.NewIntOrStringFromInt(int(c.RandUint64())) } else { rollingUpdate.MaxSurge = util.NewIntOrStringFromString(fmt.Sprintf("%d%%", c.RandUint64())) } j.RollingUpdate = &rollingUpdate } }, func(j *extensions.JobSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again completions := c.Rand.Int() parallelism := c.Rand.Int() j.Completions = &completions j.Parallelism = ¶llelism }, func(j *api.List, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // TODO: uncomment when round trip starts from a versioned object if false { //j.Items == nil { j.Items = []runtime.Object{} } }, func(j *runtime.Object, c fuzz.Continue) { // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"}, RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} t := types[c.Rand.Intn(len(types))] c.Fuzz(t) *j = t } }, func(pb map[docker.Port][]docker.PortBinding, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pb[docker.Port(c.RandString())] = []docker.PortBinding{ {c.RandString(), c.RandString()}, {c.RandString(), c.RandString()}, } }, func(pm map[string]docker.PortMapping, c fuzz.Continue) { // This is necessary because keys with nil values get omitted. // TODO: Is this a bug? pm[c.RandString()] = docker.PortMapping{ c.RandString(): c.RandString(), } }, func(q *resource.Quantity, c fuzz.Continue) { // Real Quantity fuzz testing is done elsewhere; // this limited subset of functionality survives // round-tripping to v1beta1/2. q.Amount = &inf.Dec{} q.Format = resource.DecimalExponent //q.Amount.SetScale(inf.Scale(-c.Intn(12))) q.Amount.SetUnscaled(c.Int63n(1000)) }, func(q *api.ResourceRequirements, c fuzz.Continue) { randomQuantity := func() resource.Quantity { return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) } q.Limits = make(api.ResourceList) q.Requests = make(api.ResourceList) cpuLimit := randomQuantity() q.Limits[api.ResourceCPU] = *cpuLimit.Copy() q.Requests[api.ResourceCPU] = *cpuLimit.Copy() memoryLimit := randomQuantity() q.Limits[api.ResourceMemory] = *memoryLimit.Copy() q.Requests[api.ResourceMemory] = *memoryLimit.Copy() storageLimit := randomQuantity() q.Limits[api.ResourceStorage] = *storageLimit.Copy() q.Requests[api.ResourceStorage] = *storageLimit.Copy() }, func(q *api.LimitRangeItem, c fuzz.Continue) { randomQuantity := func() resource.Quantity { return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) } cpuLimit := randomQuantity() q.Type = api.LimitTypeContainer q.Default = make(api.ResourceList) q.Default[api.ResourceCPU] = *(cpuLimit.Copy()) q.DefaultRequest = make(api.ResourceList) q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy()) q.Max = make(api.ResourceList) q.Max[api.ResourceCPU] = *(cpuLimit.Copy()) q.Min = make(api.ResourceList) q.Min[api.ResourceCPU] = *(cpuLimit.Copy()) q.MaxLimitRequestRatio = make(api.ResourceList) q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10") }, func(p *api.PullPolicy, c fuzz.Continue) { policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent} *p = policies[c.Rand.Intn(len(policies))] }, func(rp *api.RestartPolicy, c fuzz.Continue) { policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure} *rp = policies[c.Rand.Intn(len(policies))] }, func(vs *api.VolumeSource, c fuzz.Continue) { // Exactly one of the fields must be set. v := reflect.ValueOf(vs).Elem() i := int(c.RandUint64() % uint64(v.NumField())) v = v.Field(i).Addr() // Use a new fuzzer which cannot populate nil to ensure one field will be set. f := fuzz.New().NilChance(0).NumElements(1, 1) f.Funcs( // Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be // defaulted to a version otherwise roundtrip will fail // For the remaining volume plugins the default fuzzer is enough. func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) { m.Path = c.RandString() versions := []string{"v1"} m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] m.FieldRef.FieldPath = c.RandString() }, ).Fuzz(v.Interface()) }, func(d *api.DNSPolicy, c fuzz.Continue) { policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault} *d = policies[c.Rand.Intn(len(policies))] }, func(p *api.Protocol, c fuzz.Continue) { protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP} *p = protocols[c.Rand.Intn(len(protocols))] }, func(p *api.ServiceAffinity, c fuzz.Continue) { types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone} *p = types[c.Rand.Intn(len(types))] }, func(p *api.ServiceType, c fuzz.Continue) { types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer} *p = types[c.Rand.Intn(len(types))] }, func(ct *api.Container, c fuzz.Continue) { c.FuzzNoCustom(ct) // fuzz self without calling this function again ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty }, func(ev *api.EnvVar, c fuzz.Continue) { ev.Name = c.RandString() if c.RandBool() { ev.Value = c.RandString() } else { ev.ValueFrom = &api.EnvVarSource{} ev.ValueFrom.FieldRef = &api.ObjectFieldSelector{} versions := registered.RegisteredVersions ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] ev.ValueFrom.FieldRef.FieldPath = c.RandString() } }, func(sc *api.SecurityContext, c fuzz.Continue) { c.FuzzNoCustom(sc) // fuzz self without calling this function again if c.RandBool() { priv := c.RandBool() sc.Privileged = &priv } if c.RandBool() { sc.Capabilities = &api.Capabilities{ Add: make([]api.Capability, 0), Drop: make([]api.Capability, 0), } c.Fuzz(&sc.Capabilities.Add) c.Fuzz(&sc.Capabilities.Drop) } }, func(e *api.Event, c fuzz.Continue) { c.FuzzNoCustom(e) // fuzz self without calling this function again // Fix event count to 1, otherwise, if a v1beta1 or v1beta2 event has a count set arbitrarily, it's count is ignored if e.FirstTimestamp.IsZero() { e.Count = 1 } else { c.Fuzz(&e.Count) } }, func(s *api.Secret, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again s.Type = api.SecretTypeOpaque }, func(pv *api.PersistentVolume, c fuzz.Continue) { c.FuzzNoCustom(pv) // fuzz self without calling this function again types := []api.PersistentVolumePhase{api.VolumeAvailable, api.VolumePending, api.VolumeBound, api.VolumeReleased, api.VolumeFailed} pv.Status.Phase = types[c.Rand.Intn(len(types))] pv.Status.Message = c.RandString() reclamationPolicies := []api.PersistentVolumeReclaimPolicy{api.PersistentVolumeReclaimRecycle, api.PersistentVolumeReclaimRetain} pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))] }, func(pvc *api.PersistentVolumeClaim, c fuzz.Continue) { c.FuzzNoCustom(pvc) // fuzz self without calling this function again types := []api.PersistentVolumeClaimPhase{api.ClaimBound, api.ClaimPending} pvc.Status.Phase = types[c.Rand.Intn(len(types))] }, func(s *api.NamespaceSpec, c fuzz.Continue) { s.Finalizers = []api.FinalizerName{api.FinalizerKubernetes} }, func(s *api.NamespaceStatus, c fuzz.Continue) { s.Phase = api.NamespaceActive }, func(http *api.HTTPGetAction, c fuzz.Continue) { c.FuzzNoCustom(http) // fuzz self without calling this function again http.Path = "/" + http.Path // can't be blank http.Scheme = "x" + http.Scheme // can't be blank }, func(ss *api.ServiceSpec, c fuzz.Continue) { c.FuzzNoCustom(ss) // fuzz self without calling this function again if len(ss.Ports) == 0 { // There must be at least 1 port. ss.Ports = append(ss.Ports, api.ServicePort{}) c.Fuzz(&ss.Ports[0]) } for i := range ss.Ports { switch ss.Ports[i].TargetPort.Kind { case util.IntstrInt: ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero case util.IntstrString: ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty } } }, func(n *api.Node, c fuzz.Continue) { c.FuzzNoCustom(n) n.Spec.ExternalID = "external" }, func(s *extensions.APIVersion, c fuzz.Continue) { // We can't use c.RandString() here because it may generate empty // string, which will cause tests failure. s.APIGroup = "something" }, func(s *extensions.HorizontalPodAutoscalerSpec, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again minReplicas := c.Rand.Int() s.MinReplicas = &minReplicas s.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int(c.RandUint64())} }, ) return f }
// DefaultLogMaxSize returns the maximal log file size before rotation func DefaultLogMaxSize() resource.Quantity { return *resource.NewQuantity(10*1024*1024, resource.BinarySI) }
ObjectMeta: api.ObjectMeta{ Name: name, Labels: map[string]string{ "name": "foo", "time": value, }, }, Spec: api.PodSpec{ Containers: []api.Container{ { Name: "nginx", Image: "gcr.io/google_containers/pause", Resources: api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceCPU: *resource.NewMilliQuantity(100, resource.DecimalSI), api.ResourceMemory: *resource.NewQuantity(10*1024*1024, resource.DecimalSI), }, }, }, }, }, } defer podClient.Delete(pod.Name, nil) _, err := podClient.Create(pod) if err != nil { Failf("Error creating a pod: %v", err) } expectNoError(framework.WaitForPodRunning(pod.Name)) }) It("should be submitted and removed [Conformance]", func() {
func (ir initialResources) getEstimation(kind api.ResourceName, c *api.Container, ns string) (*resource.Quantity, error) { end := time.Now() start := end.Add(-week) var usage, samples int64 var err error // Historical data from last 7 days for the same image:tag within the same namespace. if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, c.Image, ns, true, start, end); err != nil { return nil, err } if samples < samplesThreshold { // Historical data from last 30 days for the same image:tag within the same namespace. start := end.Add(-month) if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, c.Image, ns, true, start, end); err != nil { return nil, err } } // If we are allowed to estimate only based on data from the same namespace. if ir.nsOnly { if samples < samplesThreshold { // Historical data from last 30 days for the same image within the same namespace. start := end.Add(-month) image := strings.Split(c.Image, ":")[0] if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, image, ns, false, start, end); err != nil { return nil, err } } } else { if samples < samplesThreshold { // Historical data from last 7 days for the same image:tag within all namespaces. start := end.Add(-week) if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, c.Image, "", true, start, end); err != nil { return nil, err } } if samples < samplesThreshold { // Historical data from last 30 days for the same image:tag within all namespaces. start := end.Add(-month) if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, c.Image, "", true, start, end); err != nil { return nil, err } } if samples < samplesThreshold { // Historical data from last 30 days for the same image within all namespaces. start := end.Add(-month) image := strings.Split(c.Image, ":")[0] if usage, samples, err = ir.source.GetUsagePercentile(kind, ir.percentile, image, "", false, start, end); err != nil { return nil, err } } } if samples > 0 && kind == api.ResourceCPU { return resource.NewMilliQuantity(usage, resource.DecimalSI), nil } if samples > 0 && kind == api.ResourceMemory { return resource.NewQuantity(usage, resource.DecimalSI), nil } return nil, nil }
func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore cache.Store) { goodCondition := api.NodeCondition{ Type: api.NodeReady, Status: api.ConditionTrue, Reason: fmt.Sprintf("schedulable condition"), LastHeartbeatTime: unversioned.Time{time.Now()}, } badCondition := api.NodeCondition{ Type: api.NodeReady, Status: api.ConditionUnknown, Reason: fmt.Sprintf("unschedulable condition"), LastHeartbeatTime: unversioned.Time{time.Now()}, } // Create a new schedulable node, since we're first going to apply // the unschedulable condition and verify that pods aren't scheduled. node := &api.Node{ ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-node"}, Spec: api.NodeSpec{Unschedulable: false}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{goodCondition}, }, } nodeKey, err := cache.MetaNamespaceKeyFunc(node) if err != nil { t.Fatalf("Couldn't retrieve key for node %v", node.Name) } // The test does the following for each nodeStateManager in this list: // 1. Create a new node // 2. Apply the makeUnSchedulable function // 3. Create a new pod // 4. Check that the pod doesn't get assigned to the node // 5. Apply the schedulable function // 6. Check that the pod *does* get assigned to the node // 7. Delete the pod and node. nodeModifications := []nodeStateManager{ // Test node.Spec.Unschedulable=true/false { makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Spec.Unschedulable = true if _, err := c.Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=true: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { // An unschedulable node should get deleted from the store return node == nil }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=true: %v", err) } }, makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Spec.Unschedulable = false if _, err := c.Nodes().Update(n); err != nil { t.Fatalf("Failed to update node with unschedulable=false: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Spec.Unschedulable == false }) if err != nil { t.Fatalf("Failed to observe reflected update for setting unschedulable=false: %v", err) } }, }, // Test node.Status.Conditions=ConditionTrue/Unknown { makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Status = api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{badCondition}, } if _, err = c.Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with bad status condition: %v", err) } err = waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionUnknown }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) { n.Status = api.NodeStatus{ Capacity: api.ResourceList{ api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI), }, Conditions: []api.NodeCondition{goodCondition}, } if _, err = c.Nodes().UpdateStatus(n); err != nil { t.Fatalf("Failed to update node with healthy status condition: %v", err) } waitForReflection(s, nodeKey, func(node interface{}) bool { return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionTrue }) if err != nil { t.Fatalf("Failed to observe reflected update for status condition update: %v", err) } }, }, } for i, mod := range nodeModifications { unSchedNode, err := restClient.Nodes().Create(node) if err != nil { t.Fatalf("Failed to create node: %v", err) } // Apply the unschedulable modification to the node, and wait for the reflection mod.makeUnSchedulable(t, unSchedNode, nodeStore, restClient) // Create the new pod, note that this needs to happen post unschedulable // modification or we have a race in the test. pod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-pod"}, Spec: api.PodSpec{ Containers: []api.Container{{Name: "container", Image: "kubernetes/pause:go"}}, }, } myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod) if err != nil { t.Fatalf("Failed to create pod: %v", err) } // There are no schedulable nodes - the pod shouldn't be scheduled. err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name)) if err == nil { t.Errorf("Pod scheduled successfully on unschedulable nodes") } if err != wait.ErrWaitTimeout { t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err) } else { t.Logf("Test %d: Pod did not get scheduled on an unschedulable node", i) } // Apply the schedulable modification to the node, and wait for the reflection schedNode, err := restClient.Nodes().Get(unSchedNode.Name) if err != nil { t.Fatalf("Failed to get node: %v", err) } mod.makeSchedulable(t, schedNode, nodeStore, restClient) // Wait until the pod is scheduled. err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name)) if err != nil { t.Errorf("Test %d: failed to schedule a pod: %v", i, err) } else { t.Logf("Test %d: Pod got scheduled on a schedulable node", i) } err = restClient.Pods(api.NamespaceDefault).Delete(myPod.Name, api.NewDeleteOptions(0)) if err != nil { t.Errorf("Failed to delete pod: %v", err) } err = restClient.Nodes().Delete(schedNode.Name) if err != nil { t.Errorf("Failed to delete node: %v", err) } } }