mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			182 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2017 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package pod
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 
 | |
| 	"strings"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/util/sets"
 | |
| 	"k8s.io/apimachinery/pkg/util/validation/field"
 | |
| 	"k8s.io/kubernetes/pkg/api"
 | |
| )
 | |
| 
 | |
| func TestPodSecrets(t *testing.T) {
 | |
| 	// Stub containing all possible secret references in a pod.
 | |
| 	// The names of the referenced secrets match struct paths detected by reflection.
 | |
| 	pod := &api.Pod{
 | |
| 		Spec: api.PodSpec{
 | |
| 			Containers: []api.Container{{
 | |
| 				EnvFrom: []api.EnvFromSource{{
 | |
| 					SecretRef: &api.SecretEnvSource{
 | |
| 						LocalObjectReference: api.LocalObjectReference{
 | |
| 							Name: "Spec.Containers[*].EnvFrom[*].SecretRef"}}}},
 | |
| 				Env: []api.EnvVar{{
 | |
| 					ValueFrom: &api.EnvVarSource{
 | |
| 						SecretKeyRef: &api.SecretKeySelector{
 | |
| 							LocalObjectReference: api.LocalObjectReference{
 | |
| 								Name: "Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
 | |
| 			ImagePullSecrets: []api.LocalObjectReference{{
 | |
| 				Name: "Spec.ImagePullSecrets"}},
 | |
| 			InitContainers: []api.Container{{
 | |
| 				EnvFrom: []api.EnvFromSource{{
 | |
| 					SecretRef: &api.SecretEnvSource{
 | |
| 						LocalObjectReference: api.LocalObjectReference{
 | |
| 							Name: "Spec.InitContainers[*].EnvFrom[*].SecretRef"}}}},
 | |
| 				Env: []api.EnvVar{{
 | |
| 					ValueFrom: &api.EnvVarSource{
 | |
| 						SecretKeyRef: &api.SecretKeySelector{
 | |
| 							LocalObjectReference: api.LocalObjectReference{
 | |
| 								Name: "Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
 | |
| 			Volumes: []api.Volume{{
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					AzureFile: &api.AzureFileVolumeSource{
 | |
| 						SecretName: "Spec.Volumes[*].VolumeSource.AzureFile.SecretName"}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					CephFS: &api.CephFSVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.CephFS.SecretRef"}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					FlexVolume: &api.FlexVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef"}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					Projected: &api.ProjectedVolumeSource{
 | |
| 						Sources: []api.VolumeProjection{{
 | |
| 							Secret: &api.SecretProjection{
 | |
| 								LocalObjectReference: api.LocalObjectReference{
 | |
| 									Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret"}}}}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					RBD: &api.RBDVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.RBD.SecretRef"}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					Secret: &api.SecretVolumeSource{
 | |
| 						SecretName: "Spec.Volumes[*].VolumeSource.Secret.SecretName"}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					Secret: &api.SecretVolumeSource{
 | |
| 						SecretName: "Spec.Volumes[*].VolumeSource.Secret"}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					ScaleIO: &api.ScaleIOVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					ISCSI: &api.ISCSIVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
 | |
| 				VolumeSource: api.VolumeSource{
 | |
| 					StorageOS: &api.StorageOSVolumeSource{
 | |
| 						SecretRef: &api.LocalObjectReference{
 | |
| 							Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
 | |
| 		},
 | |
| 	}
 | |
| 	extractedNames := sets.NewString()
 | |
| 	VisitPodSecretNames(pod, func(name string) bool {
 | |
| 		extractedNames.Insert(name)
 | |
| 		return true
 | |
| 	})
 | |
| 
 | |
| 	// excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
 | |
| 	excludedSecretPaths := sets.NewString(
 | |
| 		"Spec.Volumes[*].VolumeSource.CephFS.SecretFile",
 | |
| 	)
 | |
| 	// expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
 | |
| 	// every path here should be represented as an example in the Pod stub above, with the secret name set to the path.
 | |
| 	expectedSecretPaths := sets.NewString(
 | |
| 		"Spec.Containers[*].EnvFrom[*].SecretRef",
 | |
| 		"Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
 | |
| 		"Spec.ImagePullSecrets",
 | |
| 		"Spec.InitContainers[*].EnvFrom[*].SecretRef",
 | |
| 		"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.AzureFile.SecretName",
 | |
| 		"Spec.Volumes[*].VolumeSource.CephFS.SecretRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret",
 | |
| 		"Spec.Volumes[*].VolumeSource.RBD.SecretRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.Secret",
 | |
| 		"Spec.Volumes[*].VolumeSource.Secret.SecretName",
 | |
| 		"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
 | |
| 		"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
 | |
| 	)
 | |
| 	secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.Pod{}))
 | |
| 	secretPaths = secretPaths.Difference(excludedSecretPaths)
 | |
| 	if missingPaths := expectedSecretPaths.Difference(secretPaths); len(missingPaths) > 0 {
 | |
| 		t.Logf("Missing expected secret paths:\n%s", strings.Join(missingPaths.List(), "\n"))
 | |
| 		t.Error("Missing expected secret paths. Verify VisitPodSecretNames() is correctly finding the missing paths, then correct expectedSecretPaths")
 | |
| 	}
 | |
| 	if extraPaths := secretPaths.Difference(expectedSecretPaths); len(extraPaths) > 0 {
 | |
| 		t.Logf("Extra secret paths:\n%s", strings.Join(extraPaths.List(), "\n"))
 | |
| 		t.Error("Extra fields with 'secret' in the name found. Verify VisitPodSecretNames() is including these fields if appropriate, then correct expectedSecretPaths")
 | |
| 	}
 | |
| 
 | |
| 	if missingNames := expectedSecretPaths.Difference(extractedNames); len(missingNames) > 0 {
 | |
| 		t.Logf("Missing expected secret names:\n%s", strings.Join(missingNames.List(), "\n"))
 | |
| 		t.Error("Missing expected secret names. Verify the pod stub above includes these references, then verify VisitPodSecretNames() is correctly finding the missing names")
 | |
| 	}
 | |
| 	if extraNames := extractedNames.Difference(expectedSecretPaths); len(extraNames) > 0 {
 | |
| 		t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
 | |
| 		t.Error("Extra secret names extracted. Verify VisitPodSecretNames() is correctly extracting secret names")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // collectSecretPaths traverses the object, computing all the struct paths that lead to fields with "secret" in the name.
 | |
| func collectSecretPaths(t *testing.T, path *field.Path, name string, tp reflect.Type) sets.String {
 | |
| 	secretPaths := sets.NewString()
 | |
| 
 | |
| 	if tp.Kind() == reflect.Ptr {
 | |
| 		secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
 | |
| 		return secretPaths
 | |
| 	}
 | |
| 
 | |
| 	if strings.Contains(strings.ToLower(name), "secret") {
 | |
| 		secretPaths.Insert(path.String())
 | |
| 	}
 | |
| 
 | |
| 	switch tp.Kind() {
 | |
| 	case reflect.Ptr:
 | |
| 		secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
 | |
| 	case reflect.Struct:
 | |
| 		for i := 0; i < tp.NumField(); i++ {
 | |
| 			field := tp.Field(i)
 | |
| 			secretPaths.Insert(collectSecretPaths(t, path.Child(field.Name), field.Name, field.Type).List()...)
 | |
| 		}
 | |
| 	case reflect.Interface:
 | |
| 		t.Errorf("cannot find secret fields in interface{} field %s", path.String())
 | |
| 	case reflect.Map:
 | |
| 		secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
 | |
| 	case reflect.Slice:
 | |
| 		secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
 | |
| 	default:
 | |
| 		// all primitive types
 | |
| 	}
 | |
| 
 | |
| 	return secretPaths
 | |
| }
 |