package integration import ( "testing" kapi "k8s.io/kubernetes/pkg/api" apierrors "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/resource" kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "github.com/openshift/origin/pkg/cmd/server/api" "github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" overrideapi "github.com/openshift/origin/pkg/quota/admission/clusterresourceoverride/api" testutil "github.com/openshift/origin/test/util" testserver "github.com/openshift/origin/test/util/server" _ "github.com/openshift/origin/pkg/quota/admission/clusterresourceoverride/api/install" ) func TestClusterResourceOverridePluginWithNoLimits(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) config := &overrideapi.ClusterResourceOverrideConfig{ LimitCPUToMemoryPercent: 100, CPURequestToLimitPercent: 50, MemoryRequestToLimitPercent: 50, } kubeClientset := setupClusterResourceOverrideTest(t, config) podHandler := kubeClientset.Core().Pods(testutil.Namespace()) // test with no limits object present podCreated, err := podHandler.Create(testClusterResourceOverridePod("limitless", "2Gi", "1")) if err != nil { t.Fatal(err) } if memory := podCreated.Spec.Containers[0].Resources.Requests.Memory(); memory.Cmp(resource.MustParse("1Gi")) != 0 { t.Errorf("limitlesspod: Memory did not match expected 1Gi: %#v", memory) } if cpu := podCreated.Spec.Containers[0].Resources.Limits.Cpu(); cpu.Cmp(resource.MustParse("2")) != 0 { t.Errorf("limitlesspod: CPU limit did not match expected 2 core: %#v", cpu) } if cpu := podCreated.Spec.Containers[0].Resources.Requests.Cpu(); cpu.Cmp(resource.MustParse("1")) != 0 { t.Errorf("limitlesspod: CPU req did not match expected 1 core: %#v", cpu) } } func TestClusterResourceOverridePluginWithLimits(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) config := &overrideapi.ClusterResourceOverrideConfig{ LimitCPUToMemoryPercent: 100, CPURequestToLimitPercent: 50, MemoryRequestToLimitPercent: 50, } kubeClientset := setupClusterResourceOverrideTest(t, config) podHandler := kubeClientset.Core().Pods(testutil.Namespace()) limitHandler := kubeClientset.Core().LimitRanges(testutil.Namespace()) // test with limits object with defaults; // I wanted to test with a limits object without defaults to see limits forbid an empty resource spec, // but found that if defaults aren't set in the limit object, something still fills them in. // note: defaults are only used when quantities are *missing*, not when they are 0 limitItem := kapi.LimitRangeItem{ Type: kapi.LimitTypeContainer, Max: testResourceList("2Gi", "2"), Min: testResourceList("128Mi", "200m"), Default: testResourceList("512Mi", "500m"), // note: auto-filled from max if we set that; DefaultRequest: testResourceList("128Mi", "200m"), // filled from max if set, or min if that is set MaxLimitRequestRatio: kapi.ResourceList{}, } limit := &kapi.LimitRange{ ObjectMeta: kapi.ObjectMeta{Name: "limit"}, Spec: kapi.LimitRangeSpec{Limits: []kapi.LimitRangeItem{limitItem}}, } _, err := limitHandler.Create(limit) if err != nil { t.Fatal(err) } podCreated, err := podHandler.Create(testClusterResourceOverridePod("limit-with-default", "", "1")) if err != nil { t.Fatal(err) } if memory := podCreated.Spec.Containers[0].Resources.Limits.Memory(); memory.Cmp(resource.MustParse("512Mi")) != 0 { t.Errorf("limit-with-default: Memory limit did not match default 512Mi: %v", memory) } if memory := podCreated.Spec.Containers[0].Resources.Requests.Memory(); memory.Cmp(resource.MustParse("256Mi")) != 0 { t.Errorf("limit-with-default: Memory req did not match expected 256Mi: %v", memory) } if cpu := podCreated.Spec.Containers[0].Resources.Limits.Cpu(); cpu.Cmp(resource.MustParse("500m")) != 0 { t.Errorf("limit-with-default: CPU limit did not match expected 500 mcore: %v", cpu) } if cpu := podCreated.Spec.Containers[0].Resources.Requests.Cpu(); cpu.Cmp(resource.MustParse("250m")) != 0 { t.Errorf("limit-with-default: CPU req did not match expected 250 mcore: %v", cpu) } // set it up so that the overrides create resources that fail validation _, err = podHandler.Create(testClusterResourceOverridePod("limit-with-default-fail", "128Mi", "1")) if err == nil { t.Errorf("limit-with-default-fail: expected to be forbidden") } else if !apierrors.IsForbidden(err) { t.Errorf("limit-with-default-fail: unexpected error: %v", err) } } func setupClusterResourceOverrideTest(t *testing.T, pluginConfig *overrideapi.ClusterResourceOverrideConfig) kclientset.Interface { testutil.RequireEtcd(t) masterConfig, err := testserver.DefaultMasterOptions() if err != nil { t.Fatal(err) } // fill in possibly-empty config values if masterConfig.KubernetesMasterConfig == nil { masterConfig.KubernetesMasterConfig = &api.KubernetesMasterConfig{} } kubeMaster := masterConfig.KubernetesMasterConfig if kubeMaster.AdmissionConfig.PluginConfig == nil { kubeMaster.AdmissionConfig.PluginConfig = map[string]api.AdmissionPluginConfig{} } // set our config as desired kubeMaster.AdmissionConfig.PluginConfig[overrideapi.PluginName] = api.AdmissionPluginConfig{Configuration: pluginConfig} // start up a server and return useful clients to that server clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterConfig) if err != nil { t.Fatal(err) } clusterAdminKubeClientset, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Fatal(err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatal(err) } // need to create a project and return client for project admin clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatal(err) } _, err = testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, testutil.Namespace(), "peon") if err != nil { t.Fatal(err) } if err := testserver.WaitForServiceAccounts(clusterAdminKubeClientset, testutil.Namespace(), []string{bootstrappolicy.DefaultServiceAccountName}); err != nil { t.Fatal(err) } return clusterAdminKubeClientset } func testClusterResourceOverridePod(name string, memory string, cpu string) *kapi.Pod { resources := kapi.ResourceRequirements{ Limits: testResourceList(memory, cpu), Requests: kapi.ResourceList{}, } container := kapi.Container{Name: name, Image: "scratch", Resources: resources} pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{Name: name}, Spec: kapi.PodSpec{Containers: []kapi.Container{container}}, } return pod } func testResourceList(memory string, cpu string) kapi.ResourceList { list := kapi.ResourceList{} if memory != "" { list[kapi.ResourceMemory] = resource.MustParse(memory) } if cpu != "" { list[kapi.ResourceCPU] = resource.MustParse(cpu) } return list }