From f9a5aec318ba467cf625b7481ebd3bdca0b1ee1e Mon Sep 17 00:00:00 2001 From: sreeram-venkitesh Date: Wed, 25 Jun 2025 23:59:44 +0530 Subject: [PATCH] Added unit tests --- pkg/api/pod/util_test.go | 398 ++++++++++++++++++ pkg/kubelet/config/common_test.go | 11 +- .../noderestriction/admission_test.go | 4 +- 3 files changed, 406 insertions(+), 7 deletions(-) diff --git a/pkg/api/pod/util_test.go b/pkg/api/pod/util_test.go index 2d167f81189..8faf47aeac8 100644 --- a/pkg/api/pod/util_test.go +++ b/pkg/api/pod/util_test.go @@ -4701,3 +4701,401 @@ func TestValidateAllowPodLifecycleSleepActionZeroValue(t *testing.T) { }) } } + +func TestHasAPIReferences(t *testing.T) { + tests := []struct { + name string + pod *api.Pod + expectRejection bool + resource string + }{ + { + name: "Empty ServiceAccount in Static Pod", + pod: &api.Pod{Spec: api.PodSpec{}}, + expectRejection: false, + }, + { + name: "Non empty ServiceAccount", + pod: &api.Pod{Spec: api.PodSpec{ServiceAccountName: "default"}}, + expectRejection: true, + resource: "serviceaccounts", + }, + { + name: "Empty Volume list", + pod: &api.Pod{Spec: api.PodSpec{Volumes: nil}}, + expectRejection: false, + }, + { + name: "Non empty volume list with HostPath volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-hostpath", VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with EmptyDir volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-emptydir", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Secret volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-secret", VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "secrets (via secret volumes)", + }, + { + name: "Non empty volume list with ConfigMap volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-configmap", VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "configmaps (via configmap volumes)", + }, + { + name: "Non empty volume list with GCEPersistentDisk volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-gce", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with AWSElasticBlockStore volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-aws", VolumeSource: api.VolumeSource{AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with GitRepo volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-gitrepo", VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with NFS volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-nfs", VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with ISCSI volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-iscsi", VolumeSource: api.VolumeSource{ISCSI: &api.ISCSIVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Glusterfs volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-glusterfs", VolumeSource: api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "endpoints (via glusterFS volumes)", + }, + { + name: "Non empty volume list with PersistentVolumeClaim", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-pvc", VolumeSource: api.VolumeSource{PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "persistentvolumeclaims", + }, + { + name: "Non empty volume list with RBD volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-rbd", VolumeSource: api.VolumeSource{RBD: &api.RBDVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with FlexVolume volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-flexvolume", VolumeSource: api.VolumeSource{FlexVolume: &api.FlexVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Cinder volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-cinder", VolumeSource: api.VolumeSource{Cinder: &api.CinderVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with CephFS volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-cephfs", VolumeSource: api.VolumeSource{CephFS: &api.CephFSVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Flocker volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-flocker", VolumeSource: api.VolumeSource{Flocker: &api.FlockerVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with DownwardAPI volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-downwardapi", VolumeSource: api.VolumeSource{DownwardAPI: &api.DownwardAPIVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with FC volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-fc", VolumeSource: api.VolumeSource{FC: &api.FCVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with AzureFile volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-azurefile", VolumeSource: api.VolumeSource{AzureFile: &api.AzureFileVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "secrets (via azureFile volumes)", + }, + { + name: "Non empty volume list with VsphereVolume volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-vsphere", VolumeSource: api.VolumeSource{VsphereVolume: &api.VsphereVirtualDiskVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Quobyte volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-quobyte", VolumeSource: api.VolumeSource{Quobyte: &api.QuobyteVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with AzureDisk volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-azuredisk", VolumeSource: api.VolumeSource{AzureDisk: &api.AzureDiskVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with PhotonPersistentDisk volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-photon", VolumeSource: api.VolumeSource{PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Projected volume with clustertrustbundles", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{ClusterTrustBundle: &api.ClusterTrustBundleProjection{}}}}}}, + }}}, + expectRejection: true, + resource: "clustertrustbundles", + }, + { + name: "Non empty volume list with Projected volume with secrets", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{Secret: &api.SecretProjection{}}}}}}, + }}}, + expectRejection: true, + resource: "secrets (via projected volumes)", + }, + { + name: "Non empty volume list with Projected volume with configmap", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{ConfigMap: &api.ConfigMapProjection{}}}}}}, + }}}, + expectRejection: true, + resource: "configmaps (via projected volumes)", + }, + { + name: "Non empty volume list with Projected volume with serviceaccounttoken", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{ServiceAccountToken: &api.ServiceAccountTokenProjection{}}}}}}, + }}}, + expectRejection: true, + resource: "serviceaccounts (via projected volumes)", + }, + { + name: "Non empty volume list with Projected volume with downwardapi", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{DownwardAPI: &api.DownwardAPIProjection{}}}}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with Portworx volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-portworx", VolumeSource: api.VolumeSource{PortworxVolume: &api.PortworxVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with ScaleIO volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-scaleio", VolumeSource: api.VolumeSource{ScaleIO: &api.ScaleIOVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with StorageOS volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-storageos", VolumeSource: api.VolumeSource{StorageOS: &api.StorageOSVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "Non empty volume list with CSI volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-csi", VolumeSource: api.VolumeSource{CSI: &api.CSIVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "csidrivers (via CSI volumes)", + }, + { + name: "Non empty volume list with Ephemeral volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-ephemeral", VolumeSource: api.VolumeSource{Ephemeral: &api.EphemeralVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "persistentvolumeclaims (via ephemeral volumes)", + }, + { + name: "Non empty volume list with Image volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-image", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{}}}, + }}}, + expectRejection: false, + }, + { + name: "No envs", + pod: &api.Pod{Spec: api.PodSpec{}}, + expectRejection: false, + }, + { + name: "Non empty Env with value", + pod: &api.Pod{Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test-container", + Env: []api.EnvVar{ + { + Name: "test-env", + Value: "TEST_ENV_VAL", + }, + }, + }, + }, + }}, + expectRejection: false, + }, + { + name: "Non empty EnvFrom with ConfigMap", + pod: &api.Pod{Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test-container", + EnvFrom: []api.EnvFromSource{ + {ConfigMapRef: &api.ConfigMapEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "test"}}}, + }, + }, + }, + }}, + expectRejection: true, + resource: "configmaps", + }, + { + name: "Non empty EnvFrom with Secret", + pod: &api.Pod{Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test-container", + EnvFrom: []api.EnvFromSource{ + {SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "test"}}}, + }, + }, + }, + }}, + expectRejection: true, + resource: "secrets", + }, + { + name: "Non empty Env with ConfigMap", + pod: &api.Pod{Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test-container", + Env: []api.EnvVar{ + { + Name: "test-env", + ValueFrom: &api.EnvVarSource{ConfigMapKeyRef: &api.ConfigMapKeySelector{LocalObjectReference: api.LocalObjectReference{Name: "test"}}}, + }, + }, + }, + }, + }}, + expectRejection: true, + resource: "configmaps", + }, + { + name: "Non empty Env with Secret", + pod: &api.Pod{Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test-container", + Env: []api.EnvVar{ + { + Name: "test-env", + ValueFrom: &api.EnvVarSource{SecretKeyRef: &api.SecretKeySelector{LocalObjectReference: api.LocalObjectReference{Name: "test"}}}, + }, + }, + }, + }, + }}, + expectRejection: true, + resource: "secrets", + }, + { + name: "Multiple volume list where invalid volume comes after valid volume source", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-portworx", VolumeSource: api.VolumeSource{PortworxVolume: &api.PortworxVolumeSource{}}}, + {Name: "test-volume-configmap", VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "configmaps (via configmap volumes)", + }, + { + name: "Multiple volume list where invalid configmap volume comes after valid downwardapi projected volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{DownwardAPI: &api.DownwardAPIProjection{}}}}}}, + {Name: "test-volume-configmap", VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{}}}, + }}}, + expectRejection: true, + resource: "configmaps (via configmap volumes)", + }, + { + name: "Multiple volume list where invalid configmap projected volume comes after valid downwardapi projected volume", + pod: &api.Pod{Spec: api.PodSpec{Volumes: []api.Volume{ + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{DownwardAPI: &api.DownwardAPIProjection{}}}}}}, + {Name: "test-volume-projected", VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{ConfigMap: &api.ConfigMapProjection{}}}}}}, + }}}, + expectRejection: true, + resource: "configmaps (via projected volumes)", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actualResult, resource, _ := HasAPIObjectReference(test.pod) + if test.expectRejection != actualResult || resource != test.resource { + t.Errorf("unexpected result, expected %v but got %v, expected resource %v, but got %v", test.expectRejection, actualResult, test.resource, resource) + } + }) + } +} diff --git a/pkg/kubelet/config/common_test.go b/pkg/kubelet/config/common_test.go index e58de0c1c9f..4a7c98250ca 100644 --- a/pkg/kubelet/config/common_test.go +++ b/pkg/kubelet/config/common_test.go @@ -17,8 +17,9 @@ limitations under the License. package config import ( - "errors" + "fmt" "reflect" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -175,8 +176,8 @@ func TestDecodeSinglePodRejectsClusterTrustBundleVolumes(t *testing.T) { t.Errorf("unexpected error: %v", err) } _, _, err = tryDecodeSinglePod(json, noDefault) - if !errors.Is(err, ErrStaticPodTriedToUseClusterTrustBundle) { - t.Errorf("Got error %q, want %q", err, ErrStaticPodTriedToUseClusterTrustBundle) + if !strings.Contains(err.Error(), "may not reference clustertrustbundles") { + t.Errorf("Got error %q, want %q", err, fmt.Errorf("static pods may not reference clustertrustbundles API objects")) } } @@ -231,8 +232,8 @@ func TestDecodeSinglePodRejectsResourceClaims(t *testing.T) { t.Errorf("unexpected error: %v", err) } _, _, err = tryDecodeSinglePod(json, noDefault) - if !errors.Is(err, ErrStaticPodTriedToUseResourceClaims) { - t.Errorf("Got error %q, want %q", err, ErrStaticPodTriedToUseResourceClaims) + if !strings.Contains(err.Error(), "may not reference resourceclaims") { + t.Errorf("Got error %q, want %q", err, fmt.Errorf("static pods may not reference resourceclaims API objects")) } } diff --git a/plugin/pkg/admission/noderestriction/admission_test.go b/plugin/pkg/admission/noderestriction/admission_test.go index 0cd7c881f1a..4bd147d6d12 100644 --- a/plugin/pkg/admission/noderestriction/admission_test.go +++ b/plugin/pkg/admission/noderestriction/admission_test.go @@ -1032,7 +1032,7 @@ func Test_nodePlugin_Admit(t *testing.T) { name: "forbid create of pod referencing service account", podsGetter: noExistingPods, attributes: admission.NewAttributesRecord(sapod, nil, podKind, sapod.Namespace, sapod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), - err: "reference a service account", + err: "can not create pods that reference serviceaccounts", }, { name: "forbid create of pod referencing secret", @@ -1056,7 +1056,7 @@ func Test_nodePlugin_Admit(t *testing.T) { name: "forbid create of pod referencing persistentvolumeclaim", podsGetter: noExistingPods, attributes: admission.NewAttributesRecord(pvcpod, nil, podKind, pvcpod.Namespace, pvcpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), - err: "reference persistentvolumeclaims", + err: "can not create pods that reference persistentvolumeclaims", }, { name: "forbid create of pod referencing resourceclaim",