package admission import ( "fmt" "testing" kadmission "k8s.io/kubernetes/pkg/admission" kapi "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" clientsetfake "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "github.com/openshift/origin/pkg/image/admission/testutil" imageapi "github.com/openshift/origin/pkg/image/api" ) func TestAdmitImageStreamMapping(t *testing.T) { tests := map[string]struct { imageStreamMapping *imageapi.ImageStreamMapping limitRange *kapi.LimitRange shouldAdmit bool operation kadmission.Operation }{ "new ism, no limit range": { imageStreamMapping: getImageStreamMapping(), operation: kadmission.Create, shouldAdmit: true, }, "new ism, under limit range": { imageStreamMapping: getImageStreamMapping(), limitRange: getLimitRange("1Ki"), operation: kadmission.Create, shouldAdmit: true, }, "new ism, over limit range": { imageStreamMapping: getImageStreamMapping(), limitRange: getLimitRange("0Ki"), operation: kadmission.Create, shouldAdmit: false, }, } for k, v := range tests { var fakeKubeClient clientset.Interface if v.limitRange != nil { fakeKubeClient = clientsetfake.NewSimpleClientset(v.limitRange) } else { fakeKubeClient = clientsetfake.NewSimpleClientset() } plugin, err := NewImageLimitRangerPlugin(fakeKubeClient, nil) if err != nil { t.Errorf("%s failed creating plugin %v", k, err) continue } attrs := kadmission.NewAttributesRecord(v.imageStreamMapping, nil, imageapi.Kind("ImageStreamMapping").WithVersion("version"), v.imageStreamMapping.Namespace, v.imageStreamMapping.Name, imageapi.Resource("imagestreammappings").WithVersion("version"), "", v.operation, nil) err = plugin.Admit(attrs) if v.shouldAdmit && err != nil { t.Errorf("%s expected to be admitted but received error %v", k, err) } if !v.shouldAdmit && err == nil { t.Errorf("%s expected to be rejected but received no error", k) } } } func TestAdmitImage(t *testing.T) { tests := map[string]struct { size resource.Quantity limitSize resource.Quantity shouldAdmit bool limitRangeItem *kapi.LimitRangeItem }{ "under size": { size: resource.MustParse("50Mi"), limitSize: resource.MustParse("100Mi"), shouldAdmit: true, }, "equal size": { size: resource.MustParse("100Mi"), limitSize: resource.MustParse("100Mi"), shouldAdmit: true, }, "over size": { size: resource.MustParse("101Mi"), limitSize: resource.MustParse("100Mi"), shouldAdmit: false, }, "non-applicable limit range item": { size: resource.MustParse("100Mi"), limitRangeItem: &kapi.LimitRangeItem{ Type: kapi.LimitTypeContainer, }, shouldAdmit: true, }, } for k, v := range tests { limitRangeItem := v.limitRangeItem if limitRangeItem == nil { limitRangeItem = &kapi.LimitRangeItem{ Type: imageapi.LimitTypeImage, Max: kapi.ResourceList{ kapi.ResourceStorage: v.limitSize, }, } } err := AdmitImage(v.size.Value(), *limitRangeItem) if v.shouldAdmit && err != nil { t.Errorf("%s expected to be admitted but received error %v", k, err) } if !v.shouldAdmit && err == nil { t.Errorf("%s expected to be denied but was admitted", k) } } } func TestLimitAppliestoImages(t *testing.T) { tests := map[string]struct { limitRange *kapi.LimitRange shouldApply bool }{ "good limit range": { limitRange: &kapi.LimitRange{ Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{ { Type: imageapi.LimitTypeImage, }, }, }, }, shouldApply: true, }, "bad limit range": { limitRange: &kapi.LimitRange{ Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{ { Type: kapi.LimitTypeContainer, }, }, }, }, shouldApply: false, }, "malformed range with no type": { limitRange: &kapi.LimitRange{ Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{}, }, }, shouldApply: false, }, "malformed range with no limits": { limitRange: &kapi.LimitRange{}, shouldApply: false, }, } plugin, err := NewImageLimitRangerPlugin(clientsetfake.NewSimpleClientset(), nil) if err != nil { t.Fatalf("error creating plugin: %v", err) } ilr := plugin.(*imageLimitRangerPlugin) for k, v := range tests { supports := ilr.SupportsLimit(v.limitRange) if supports && !v.shouldApply { t.Errorf("%s expected limit range to not be applicable", k) } if !supports && v.shouldApply { t.Errorf("%s expected limit range to be applicable", k) } } } func TestHandles(t *testing.T) { plugin, err := NewImageLimitRangerPlugin(clientsetfake.NewSimpleClientset(), nil) if err != nil { t.Fatalf("error creating plugin: %v", err) } if !plugin.Handles(kadmission.Create) { t.Errorf("plugin is expected to handle create") } if plugin.Handles(kadmission.Update) { t.Errorf("plugin is not expected to handle update") } if plugin.Handles(kadmission.Delete) { t.Errorf("plugin is not expected to handle delete") } if plugin.Handles(kadmission.Connect) { t.Errorf("plugin is expected to handle connect") } } func TestSupports(t *testing.T) { resources := []string{"imagestreammappings"} plugin, err := NewImageLimitRangerPlugin(clientsetfake.NewSimpleClientset(), nil) if err != nil { t.Fatalf("error creating plugin: %v", err) } ilr := plugin.(*imageLimitRangerPlugin) for _, r := range resources { attr := kadmission.NewAttributesRecord(nil, nil, unversioned.Kind("ImageStreamMapping").WithVersion("version"), "ns", "name", imageapi.Resource(r).WithVersion("version"), "", kadmission.Create, nil) if !ilr.SupportsAttributes(attr) { t.Errorf("plugin is expected to support %s", r) } } badKinds := []string{"ImageStream", "Image", "Pod", "foo"} for _, k := range badKinds { attr := kadmission.NewAttributesRecord(nil, nil, unversioned.Kind(k).WithVersion("version"), "ns", "name", imageapi.Resource("bar").WithVersion("version"), "", kadmission.Create, nil) if ilr.SupportsAttributes(attr) { t.Errorf("plugin is not expected to support %s", k) } } } func getBaseImageWith1Layer() imageapi.Image { return imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: testutil.BaseImageWith1LayerDigest, Annotations: map[string]string{imageapi.ManagedByOpenShiftAnnotation: "true"}, }, DockerImageReference: fmt.Sprintf("registry.example.org/%s/%s", "test", testutil.BaseImageWith1LayerDigest), DockerImageManifest: testutil.BaseImageWith1Layer, } } func getLimitRange(limit string) *kapi.LimitRange { return &kapi.LimitRange{ ObjectMeta: kapi.ObjectMeta{ Name: "test-limit", Namespace: "test", }, Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{ { Type: imageapi.LimitTypeImage, Max: kapi.ResourceList{ kapi.ResourceStorage: resource.MustParse(limit), }, }, }, }, } } func getImageStreamMapping() *imageapi.ImageStreamMapping { return &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Name: "test-ism", Namespace: "test", }, Image: getBaseImageWith1Layer(), } }