2017-02-27 06:00:58 +00:00
/ *
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 {
2016-11-19 20:46:23 +00:00
SecretName : "Spec.Volumes[*].VolumeSource.Secret" } } } , {
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
SecretRef : & api . LocalObjectReference {
2017-03-17 20:42:15 +00:00
Name : "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef" } } } } , {
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
SecretRef : & api . LocalObjectReference {
2017-02-24 15:47:40 +00:00
Name : "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef" } } } } , {
VolumeSource : api . VolumeSource {
StorageOS : & api . StorageOSVolumeSource {
SecretRef : & api . LocalObjectReference {
Name : "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef" } } } } } ,
2017-02-27 06:00:58 +00:00
} ,
}
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" ,
2016-11-19 20:46:23 +00:00
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef" ,
2017-03-17 20:42:15 +00:00
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef" ,
2017-02-24 15:47:40 +00:00
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef" ,
2017-02-27 06:00:58 +00:00
)
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
}