2017-02-27 01:00:58 -05: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 (
2018-12-18 10:10:56 -05:00
"fmt"
2017-02-27 01:00:58 -05:00
"reflect"
"strings"
2018-08-17 05:03:30 -04:00
"testing"
2017-02-27 01:00:58 -05:00
2020-03-20 13:21:24 -04:00
"github.com/google/go-cmp/cmp"
2020-03-18 10:51:47 -04:00
2020-07-17 14:56:52 -04:00
v1 "k8s.io/api/core/v1"
2018-12-27 10:30:37 -05:00
"k8s.io/apimachinery/pkg/api/resource"
2019-01-07 15:10:11 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-02-27 01:00:58 -05:00
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
2017-08-09 13:51:46 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2019-03-04 12:46:52 -05:00
featuregatetesting "k8s.io/component-base/featuregate/testing"
2017-11-08 17:34:54 -05:00
api "k8s.io/kubernetes/pkg/apis/core"
2017-08-09 13:51:46 -04:00
"k8s.io/kubernetes/pkg/features"
2017-02-27 01:00:58 -05:00
)
2019-06-14 11:20:16 -04:00
func TestVisitContainers ( t * testing . T ) {
2021-10-04 05:44:19 -04:00
setAllFeatureEnabledContainersDuringTest := ContainerType ( 0 )
2019-06-14 11:20:16 -04:00
testCases := [ ] struct {
2020-03-20 13:21:24 -04:00
desc string
spec * api . PodSpec
wantContainers [ ] string
mask ContainerType
ephemeralContainersEnabled bool
2019-06-14 11:20:16 -04:00
} {
{
2020-03-20 13:21:24 -04:00
desc : "empty podspec" ,
spec : & api . PodSpec { } ,
wantContainers : [ ] string { } ,
mask : AllContainers ,
2019-06-14 11:20:16 -04:00
} ,
{
2020-03-20 13:21:24 -04:00
desc : "regular containers" ,
spec : & api . PodSpec {
2019-06-14 11:20:16 -04:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" } ,
} ,
2020-03-20 13:21:24 -04:00
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" } ,
} ,
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
} ,
2019-06-14 11:20:16 -04:00
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "c1" , "c2" } ,
mask : Containers ,
2019-06-14 11:20:16 -04:00
} ,
{
2020-03-20 13:21:24 -04:00
desc : "init containers" ,
spec : & api . PodSpec {
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" } ,
} ,
2019-06-14 11:20:16 -04:00
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" } ,
} ,
2020-03-20 13:21:24 -04:00
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
} ,
2019-06-14 11:20:16 -04:00
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "i1" , "i2" } ,
mask : InitContainers ,
2019-06-14 11:20:16 -04:00
} ,
{
2020-03-20 13:21:24 -04:00
desc : "ephemeral containers" ,
spec : & api . PodSpec {
2019-06-14 11:20:16 -04:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" } ,
} ,
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" } ,
} ,
2020-03-20 13:21:24 -04:00
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
} ,
2019-06-14 11:20:16 -04:00
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "e1" , "e2" } ,
mask : EphemeralContainers ,
2019-06-14 11:20:16 -04:00
} ,
2018-08-09 09:24:23 -04:00
{
2020-03-20 13:21:24 -04:00
desc : "all container types" ,
spec : & api . PodSpec {
2018-08-09 09:24:23 -04:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" } ,
} ,
2020-03-20 13:21:24 -04:00
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" } ,
} ,
2018-08-09 09:24:23 -04:00
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
2020-03-20 13:21:24 -04:00
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
2018-08-09 09:24:23 -04:00
} ,
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "i1" , "i2" , "c1" , "c2" , "e1" , "e2" } ,
mask : AllContainers ,
2018-08-09 09:24:23 -04:00
} ,
{
2020-03-20 13:21:24 -04:00
desc : "all feature enabled container types with ephemeral containers disabled" ,
spec : & api . PodSpec {
2018-08-09 09:24:23 -04:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" } ,
} ,
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" } ,
} ,
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
} ,
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "i1" , "i2" , "c1" , "c2" } ,
2021-10-04 05:44:19 -04:00
mask : setAllFeatureEnabledContainersDuringTest ,
2020-03-05 14:48:00 -05:00
} ,
{
2020-03-20 13:21:24 -04:00
desc : "all feature enabled container types with ephemeral containers enabled" ,
spec : & api . PodSpec {
2020-03-05 14:48:00 -05:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
2020-03-20 13:21:24 -04:00
{ Name : "c2" , SecurityContext : & api . SecurityContext { } } ,
2020-03-05 14:48:00 -05:00
} ,
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
2020-03-20 13:21:24 -04:00
{ Name : "i2" , SecurityContext : & api . SecurityContext { } } ,
2020-03-05 14:48:00 -05:00
} ,
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" } } ,
} ,
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "i1" , "i2" , "c1" , "c2" , "e1" , "e2" } ,
2021-10-04 05:44:19 -04:00
mask : setAllFeatureEnabledContainersDuringTest ,
2020-03-20 13:21:24 -04:00
ephemeralContainersEnabled : true ,
2018-08-09 09:24:23 -04:00
} ,
2019-06-14 11:20:16 -04:00
{
2020-03-20 13:21:24 -04:00
desc : "dropping fields" ,
spec : & api . PodSpec {
2019-06-14 11:20:16 -04:00
Containers : [ ] api . Container {
{ Name : "c1" } ,
{ Name : "c2" , SecurityContext : & api . SecurityContext { } } ,
} ,
InitContainers : [ ] api . Container {
{ Name : "i1" } ,
{ Name : "i2" , SecurityContext : & api . SecurityContext { } } ,
} ,
2018-08-09 09:24:23 -04:00
EphemeralContainers : [ ] api . EphemeralContainer {
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e1" } } ,
{ EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "e2" , SecurityContext : & api . SecurityContext { } } } ,
} ,
2019-06-14 11:20:16 -04:00
} ,
2020-03-20 13:21:24 -04:00
wantContainers : [ ] string { "i1" , "i2" , "c1" , "c2" , "e1" , "e2" } ,
mask : AllContainers ,
2019-06-14 11:20:16 -04:00
} ,
}
for _ , tc := range testCases {
2020-03-20 13:21:24 -04:00
t . Run ( tc . desc , func ( t * testing . T ) {
2021-10-04 05:44:19 -04:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . EphemeralContainers , tc . ephemeralContainersEnabled ) ( )
if tc . mask == setAllFeatureEnabledContainersDuringTest {
2020-03-20 13:21:24 -04:00
tc . mask = AllFeatureEnabledContainers ( )
2019-06-14 11:20:16 -04:00
}
2020-03-20 13:21:24 -04:00
gotContainers := [ ] string { }
VisitContainers ( tc . spec , tc . mask , func ( c * api . Container , containerType ContainerType ) bool {
gotContainers = append ( gotContainers , c . Name )
if c . SecurityContext != nil {
c . SecurityContext = nil
}
return true
} )
if ! cmp . Equal ( gotContainers , tc . wantContainers ) {
t . Errorf ( "VisitContainers() = %+v, want %+v" , gotContainers , tc . wantContainers )
2019-06-14 11:20:16 -04:00
}
2020-03-20 13:21:24 -04:00
for _ , c := range tc . spec . Containers {
if c . SecurityContext != nil {
t . Errorf ( "VisitContainers() did not drop SecurityContext for container %q" , c . Name )
}
2019-06-14 11:20:16 -04:00
}
2020-03-20 13:21:24 -04:00
for _ , c := range tc . spec . InitContainers {
if c . SecurityContext != nil {
t . Errorf ( "VisitContainers() did not drop SecurityContext for init container %q" , c . Name )
}
2018-08-09 09:24:23 -04:00
}
2020-03-20 13:21:24 -04:00
for _ , c := range tc . spec . EphemeralContainers {
if c . SecurityContext != nil {
t . Errorf ( "VisitContainers() did not drop SecurityContext for ephemeral container %q" , c . Name )
}
}
} )
2019-06-14 11:20:16 -04:00
}
}
2017-02-27 01:00:58 -05:00
func TestPodSecrets ( t * testing . T ) {
2018-08-09 09:24:23 -04:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . EphemeralContainers , true ) ( )
2017-02-27 01:00:58 -05:00
// 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" } } } } , {
2018-05-14 17:09:05 -04:00
VolumeSource : api . VolumeSource {
Cinder : & api . CinderVolumeSource {
SecretRef : & api . LocalObjectReference {
Name : "Spec.Volumes[*].VolumeSource.Cinder.SecretRef" } } } } , {
2017-02-27 01:00:58 -05:00
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 15:46:23 -05:00
SecretName : "Spec.Volumes[*].VolumeSource.Secret" } } } , {
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
SecretRef : & api . LocalObjectReference {
2017-03-17 16:42:15 -04:00
Name : "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef" } } } } , {
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
SecretRef : & api . LocalObjectReference {
2017-02-24 10:47:40 -05:00
Name : "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef" } } } } , {
VolumeSource : api . VolumeSource {
StorageOS : & api . StorageOSVolumeSource {
SecretRef : & api . LocalObjectReference {
2018-08-14 17:00:25 -04:00
Name : "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef" } } } } , {
VolumeSource : api . VolumeSource {
CSI : & api . CSIVolumeSource {
NodePublishSecretRef : & api . LocalObjectReference {
Name : "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef" } } } } } ,
2018-08-09 09:24:23 -04:00
EphemeralContainers : [ ] api . EphemeralContainer { {
EphemeralContainerCommon : api . EphemeralContainerCommon {
EnvFrom : [ ] api . EnvFromSource { {
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef" } } } } ,
Env : [ ] api . EnvVar { {
ValueFrom : & api . EnvVarSource {
SecretKeyRef : & api . SecretKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef" } } } } } } } } ,
2017-02-27 01:00:58 -05:00
} ,
}
extractedNames := sets . NewString ( )
VisitPodSecretNames ( pod , func ( name string ) bool {
extractedNames . Insert ( name )
return true
2020-03-20 10:59:44 -04:00
} , AllContainers )
2017-02-27 01:00:58 -05:00
// 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" ,
2018-08-09 09:24:23 -04:00
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef" ,
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef" ,
2017-02-27 01:00:58 -05:00
"Spec.ImagePullSecrets" ,
"Spec.InitContainers[*].EnvFrom[*].SecretRef" ,
"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef" ,
"Spec.Volumes[*].VolumeSource.AzureFile.SecretName" ,
"Spec.Volumes[*].VolumeSource.CephFS.SecretRef" ,
2018-05-14 17:09:05 -04:00
"Spec.Volumes[*].VolumeSource.Cinder.SecretRef" ,
2017-02-27 01:00:58 -05:00
"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 15:46:23 -05:00
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef" ,
2017-03-17 16:42:15 -04:00
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef" ,
2017-02-24 10:47:40 -05:00
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef" ,
2018-08-14 17:00:25 -04:00
"Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef" ,
2017-02-27 01:00:58 -05:00
)
2017-11-10 02:44:51 -05:00
secretPaths := collectResourcePaths ( t , "secret" , nil , "" , reflect . TypeOf ( & api . Pod { } ) )
2017-02-27 01:00:58 -05:00
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" )
}
2021-02-27 14:09:57 -05:00
// emptyPod is a stub containing empty object names
emptyPod := & api . Pod {
Spec : api . PodSpec {
Containers : [ ] api . Container { {
EnvFrom : [ ] api . EnvFromSource { {
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "" } } } } } } ,
} ,
}
VisitPodSecretNames ( emptyPod , func ( name string ) bool {
t . Fatalf ( "expected no empty names collected, got %q" , name )
return false
} , AllContainers )
2017-02-27 01:00:58 -05:00
}
2017-11-10 02:44:51 -05:00
// collectResourcePaths traverses the object, computing all the struct paths that lead to fields with resourcename in the name.
func collectResourcePaths ( t * testing . T , resourcename string , path * field . Path , name string , tp reflect . Type ) sets . String {
resourcename = strings . ToLower ( resourcename )
resourcePaths := sets . NewString ( )
2017-02-27 01:00:58 -05:00
if tp . Kind ( ) == reflect . Ptr {
2017-11-10 02:44:51 -05:00
resourcePaths . Insert ( collectResourcePaths ( t , resourcename , path , name , tp . Elem ( ) ) . List ( ) ... )
return resourcePaths
2017-02-27 01:00:58 -05:00
}
2017-11-10 02:44:51 -05:00
if strings . Contains ( strings . ToLower ( name ) , resourcename ) {
resourcePaths . Insert ( path . String ( ) )
2017-02-27 01:00:58 -05:00
}
switch tp . Kind ( ) {
case reflect . Ptr :
2017-11-10 02:44:51 -05:00
resourcePaths . Insert ( collectResourcePaths ( t , resourcename , path , name , tp . Elem ( ) ) . List ( ) ... )
2017-02-27 01:00:58 -05:00
case reflect . Struct :
2019-02-01 14:55:18 -05:00
// ObjectMeta is generic and therefore should never have a field with a specific resource's name;
// it contains cycles so it's easiest to just skip it.
2019-01-17 00:14:42 -05:00
if name == "ObjectMeta" {
break
}
2017-02-27 01:00:58 -05:00
for i := 0 ; i < tp . NumField ( ) ; i ++ {
field := tp . Field ( i )
2017-11-10 02:44:51 -05:00
resourcePaths . Insert ( collectResourcePaths ( t , resourcename , path . Child ( field . Name ) , field . Name , field . Type ) . List ( ) ... )
2017-02-27 01:00:58 -05:00
}
case reflect . Interface :
2017-11-10 02:44:51 -05:00
t . Errorf ( "cannot find %s fields in interface{} field %s" , resourcename , path . String ( ) )
2017-02-27 01:00:58 -05:00
case reflect . Map :
2017-11-10 02:44:51 -05:00
resourcePaths . Insert ( collectResourcePaths ( t , resourcename , path . Key ( "*" ) , "" , tp . Elem ( ) ) . List ( ) ... )
2017-02-27 01:00:58 -05:00
case reflect . Slice :
2017-11-10 02:44:51 -05:00
resourcePaths . Insert ( collectResourcePaths ( t , resourcename , path . Key ( "*" ) , "" , tp . Elem ( ) ) . List ( ) ... )
2017-02-27 01:00:58 -05:00
default :
// all primitive types
}
2017-11-10 02:44:51 -05:00
return resourcePaths
}
func TestPodConfigmaps ( t * testing . T ) {
2018-08-09 09:24:23 -04:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . EphemeralContainers , true ) ( )
2017-11-10 02:44:51 -05:00
// Stub containing all possible ConfigMap references in a pod.
// The names of the referenced ConfigMaps match struct paths detected by reflection.
pod := & api . Pod {
Spec : api . PodSpec {
Containers : [ ] api . Container { {
EnvFrom : [ ] api . EnvFromSource { {
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.Containers[*].EnvFrom[*].ConfigMapRef" } } } } ,
Env : [ ] api . EnvVar { {
ValueFrom : & api . EnvVarSource {
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef" } } } } } } } ,
2018-08-09 09:24:23 -04:00
EphemeralContainers : [ ] api . EphemeralContainer { {
EphemeralContainerCommon : api . EphemeralContainerCommon {
EnvFrom : [ ] api . EnvFromSource { {
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef" } } } } ,
Env : [ ] api . EnvVar { {
ValueFrom : & api . EnvVarSource {
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef" } } } } } } } } ,
2017-11-10 02:44:51 -05:00
InitContainers : [ ] api . Container { {
EnvFrom : [ ] api . EnvFromSource { {
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.InitContainers[*].EnvFrom[*].ConfigMapRef" } } } } ,
Env : [ ] api . EnvVar { {
ValueFrom : & api . EnvVarSource {
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef" } } } } } } } ,
Volumes : [ ] api . Volume { {
VolumeSource : api . VolumeSource {
Projected : & api . ProjectedVolumeSource {
Sources : [ ] api . VolumeProjection { {
ConfigMap : & api . ConfigMapProjection {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap" } } } } } } } , {
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference {
Name : "Spec.Volumes[*].VolumeSource.ConfigMap" } } } } } ,
} ,
}
extractedNames := sets . NewString ( )
VisitPodConfigmapNames ( pod , func ( name string ) bool {
extractedNames . Insert ( name )
return true
2020-03-20 10:59:44 -04:00
} , AllContainers )
2017-11-10 02:44:51 -05:00
// expectedPaths holds struct paths to fields with "ConfigMap" in the name that are references to ConfigMap API objects.
// every path here should be represented as an example in the Pod stub above, with the ConfigMap name set to the path.
expectedPaths := sets . NewString (
"Spec.Containers[*].EnvFrom[*].ConfigMapRef" ,
"Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef" ,
2018-08-09 09:24:23 -04:00
"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef" ,
"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef" ,
2017-11-10 02:44:51 -05:00
"Spec.InitContainers[*].EnvFrom[*].ConfigMapRef" ,
"Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef" ,
"Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap" ,
"Spec.Volumes[*].VolumeSource.ConfigMap" ,
)
collectPaths := collectResourcePaths ( t , "ConfigMap" , nil , "" , reflect . TypeOf ( & api . Pod { } ) )
if missingPaths := expectedPaths . Difference ( collectPaths ) ; len ( missingPaths ) > 0 {
t . Logf ( "Missing expected paths:\n%s" , strings . Join ( missingPaths . List ( ) , "\n" ) )
t . Error ( "Missing expected paths. Verify VisitPodConfigmapNames() is correctly finding the missing paths, then correct expectedPaths" )
}
if extraPaths := collectPaths . Difference ( expectedPaths ) ; len ( extraPaths ) > 0 {
t . Logf ( "Extra paths:\n%s" , strings . Join ( extraPaths . List ( ) , "\n" ) )
t . Error ( "Extra fields with resource in the name found. Verify VisitPodConfigmapNames() is including these fields if appropriate, then correct expectedPaths" )
}
if missingNames := expectedPaths . Difference ( extractedNames ) ; len ( missingNames ) > 0 {
t . Logf ( "Missing expected names:\n%s" , strings . Join ( missingNames . List ( ) , "\n" ) )
t . Error ( "Missing expected names. Verify the pod stub above includes these references, then verify VisitPodConfigmapNames() is correctly finding the missing names" )
}
if extraNames := extractedNames . Difference ( expectedPaths ) ; len ( extraNames ) > 0 {
t . Logf ( "Extra names:\n%s" , strings . Join ( extraNames . List ( ) , "\n" ) )
t . Error ( "Extra names extracted. Verify VisitPodConfigmapNames() is correctly extracting resource names" )
}
2021-02-27 14:09:57 -05:00
// emptyPod is a stub containing empty object names
emptyPod := & api . Pod {
Spec : api . PodSpec {
Containers : [ ] api . Container { {
EnvFrom : [ ] api . EnvFromSource { {
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "" } } } } } } ,
} ,
}
VisitPodConfigmapNames ( emptyPod , func ( name string ) bool {
t . Fatalf ( "expected no empty names collected, got %q" , name )
return false
} , AllContainers )
2017-02-27 01:00:58 -05:00
}
2017-08-09 13:51:46 -04:00
2020-02-21 16:35:52 -05:00
func TestDropFSGroupFields ( t * testing . T ) {
nofsGroupPod := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Name : "container1" ,
Image : "testimage" ,
} ,
} ,
} ,
}
}
var podFSGroup int64 = 100
changePolicy := api . FSGroupChangeAlways
fsGroupPod := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Name : "container1" ,
Image : "testimage" ,
} ,
} ,
SecurityContext : & api . PodSecurityContext {
FSGroup : & podFSGroup ,
FSGroupChangePolicy : & changePolicy ,
} ,
} ,
}
}
podInfos := [ ] struct {
description string
newPodHasFSGroupChangePolicy bool
pod func ( ) * api . Pod
expectPolicyInPod bool
} {
{
description : "oldPod.FSGroupChangePolicy=nil, feature=true, newPod.FSGroupChangePolicy=true" ,
pod : nofsGroupPod ,
newPodHasFSGroupChangePolicy : true ,
expectPolicyInPod : true ,
} ,
{
description : "oldPod=nil, feature=true, newPod.FSGroupChangePolicy=true" ,
pod : func ( ) * api . Pod { return nil } ,
newPodHasFSGroupChangePolicy : true ,
expectPolicyInPod : true ,
} ,
{
description : "oldPod.FSGroupChangePolicy=true, feature=true, newPod.FSGroupChangePolicy=false" ,
pod : fsGroupPod ,
newPodHasFSGroupChangePolicy : false ,
expectPolicyInPod : false ,
} ,
}
for _ , podInfo := range podInfos {
t . Run ( podInfo . description , func ( t * testing . T ) {
oldPod := podInfo . pod ( )
newPod := oldPod . DeepCopy ( )
if oldPod == nil && podInfo . newPodHasFSGroupChangePolicy {
newPod = fsGroupPod ( )
}
if oldPod != nil {
if podInfo . newPodHasFSGroupChangePolicy {
newPod . Spec . SecurityContext = & api . PodSecurityContext {
FSGroup : & podFSGroup ,
FSGroupChangePolicy : & changePolicy ,
}
} else {
newPod . Spec . SecurityContext = & api . PodSecurityContext { }
}
}
DropDisabledPodFields ( newPod , oldPod )
if podInfo . expectPolicyInPod {
secContext := newPod . Spec . SecurityContext
if secContext == nil || secContext . FSGroupChangePolicy == nil {
t . Errorf ( "for %s, expected fsGroupChangepolicy found none" , podInfo . description )
}
} else {
secConext := newPod . Spec . SecurityContext
if secConext != nil && secConext . FSGroupChangePolicy != nil {
t . Errorf ( "for %s, unexpected fsGroupChangepolicy set" , podInfo . description )
}
}
} )
}
}
2018-12-20 12:28:08 -05:00
func TestDropProcMount ( t * testing . T ) {
procMount := api . UnmaskedProcMount
defaultProcMount := api . DefaultProcMount
podWithProcMount := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
Containers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : & procMount } } } ,
InitContainers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : & procMount } } } ,
} ,
}
}
2019-06-10 21:47:00 -04:00
podWithDefaultProcMount := func ( ) * api . Pod {
2018-12-20 12:28:08 -05:00
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
Containers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : & defaultProcMount } } } ,
InitContainers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : & defaultProcMount } } } ,
} ,
}
}
2019-06-10 21:47:00 -04:00
podWithoutProcMount := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
Containers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : nil } } } ,
InitContainers : [ ] api . Container { { Name : "container1" , Image : "testimage" , SecurityContext : & api . SecurityContext { ProcMount : nil } } } ,
} ,
}
}
2018-12-20 12:28:08 -05:00
podInfo := [ ] struct {
description string
hasProcMount bool
pod func ( ) * api . Pod
} {
{
description : "has ProcMount" ,
hasProcMount : true ,
pod : podWithProcMount ,
} ,
2019-06-10 21:47:00 -04:00
{
description : "has default ProcMount" ,
hasProcMount : false ,
pod : podWithDefaultProcMount ,
} ,
2018-12-20 12:28:08 -05:00
{
description : "does not have ProcMount" ,
hasProcMount : false ,
pod : podWithoutProcMount ,
} ,
{
description : "is nil" ,
hasProcMount : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasProcMount , oldPod := oldPodInfo . hasProcMount , oldPodInfo . pod ( )
newPodHasProcMount , newPod := newPodInfo . hasProcMount , newPodInfo . pod ( )
if newPod == nil {
continue
}
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
2019-03-04 12:46:52 -05:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . ProcMountType , enabled ) ( )
2018-12-20 12:28:08 -05:00
var oldPodSpec * api . PodSpec
if oldPod != nil {
oldPodSpec = & oldPod . Spec
}
2019-01-07 14:17:29 -05:00
dropDisabledFields ( & newPod . Spec , nil , oldPodSpec , nil )
2018-12-20 12:28:08 -05:00
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
2018-12-20 12:28:08 -05:00
}
switch {
case enabled || oldPodHasProcMount :
// new pod should not be changed if the feature is enabled, or if the old pod had ProcMount
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2018-12-20 12:28:08 -05:00
}
case newPodHasProcMount :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have ProcMount
2019-06-10 21:47:00 -04:00
if procMountInUse ( & newPod . Spec ) {
t . Errorf ( "new pod had ProcMount: %#v" , & newPod . Spec )
2018-12-20 12:28:08 -05:00
}
default :
// new pod should not need to be changed
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2018-12-20 12:28:08 -05:00
}
}
} )
}
}
}
}
2018-12-19 14:55:34 -05:00
2018-12-27 10:30:37 -05:00
func TestDropEmptyDirSizeLimit ( t * testing . T ) {
sizeLimit := resource . MustParse ( "1Gi" )
podWithEmptyDirSizeLimit := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
Volumes : [ ] api . Volume {
{
Name : "a" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource {
Medium : "memory" ,
SizeLimit : & sizeLimit ,
} ,
} ,
} ,
} ,
} ,
}
}
podWithoutEmptyDirSizeLimit := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
Volumes : [ ] api . Volume {
{
Name : "a" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource {
Medium : "memory" ,
} ,
} ,
} ,
} ,
} ,
}
}
podInfo := [ ] struct {
description string
hasEmptyDirSizeLimit bool
pod func ( ) * api . Pod
} {
{
description : "has EmptyDir Size Limit" ,
hasEmptyDirSizeLimit : true ,
pod : podWithEmptyDirSizeLimit ,
} ,
{
description : "does not have EmptyDir Size Limit" ,
hasEmptyDirSizeLimit : false ,
pod : podWithoutEmptyDirSizeLimit ,
} ,
{
description : "is nil" ,
hasEmptyDirSizeLimit : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasEmptyDirSizeLimit , oldPod := oldPodInfo . hasEmptyDirSizeLimit , oldPodInfo . pod ( )
newPodHasEmptyDirSizeLimit , newPod := newPodInfo . hasEmptyDirSizeLimit , newPodInfo . pod ( )
if newPod == nil {
continue
}
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
2019-03-04 12:46:52 -05:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . LocalStorageCapacityIsolation , enabled ) ( )
2018-12-27 10:30:37 -05:00
var oldPodSpec * api . PodSpec
if oldPod != nil {
oldPodSpec = & oldPod . Spec
}
2019-01-07 14:17:29 -05:00
dropDisabledFields ( & newPod . Spec , nil , oldPodSpec , nil )
2018-12-27 10:30:37 -05:00
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
2018-12-27 10:30:37 -05:00
}
switch {
case enabled || oldPodHasEmptyDirSizeLimit :
// new pod should not be changed if the feature is enabled, or if the old pod had EmptyDir SizeLimit
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2018-12-27 10:30:37 -05:00
}
case newPodHasEmptyDirSizeLimit :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have EmptyDir SizeLimit
if ! reflect . DeepEqual ( newPod , podWithoutEmptyDirSizeLimit ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod had EmptyDir SizeLimit: %v" , cmp . Diff ( newPod , podWithoutEmptyDirSizeLimit ( ) ) )
2018-12-27 10:30:37 -05:00
}
default :
// new pod should not need to be changed
2019-01-07 15:10:11 -05:00
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2019-01-08 17:26:21 -05:00
}
}
} )
}
}
}
}
2019-01-07 15:10:11 -05:00
func TestDropAppArmor ( t * testing . T ) {
podWithAppArmor := func ( ) * api . Pod {
return & api . Pod {
2020-03-18 10:51:47 -04:00
ObjectMeta : metav1 . ObjectMeta { Annotations : map [ string ] string { "a" : "1" , v1 . AppArmorBetaContainerAnnotationKeyPrefix + "foo" : "default" } } ,
2019-01-07 15:10:11 -05:00
Spec : api . PodSpec { } ,
}
}
podWithoutAppArmor := func ( ) * api . Pod {
return & api . Pod {
ObjectMeta : metav1 . ObjectMeta { Annotations : map [ string ] string { "a" : "1" } } ,
Spec : api . PodSpec { } ,
}
}
podInfo := [ ] struct {
description string
hasAppArmor bool
pod func ( ) * api . Pod
} {
{
description : "has AppArmor" ,
hasAppArmor : true ,
pod : podWithAppArmor ,
} ,
{
description : "does not have AppArmor" ,
hasAppArmor : false ,
pod : podWithoutAppArmor ,
} ,
{
description : "is nil" ,
hasAppArmor : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasAppArmor , oldPod := oldPodInfo . hasAppArmor , oldPodInfo . pod ( )
newPodHasAppArmor , newPod := newPodInfo . hasAppArmor , newPodInfo . pod ( )
if newPod == nil {
continue
}
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
2019-03-04 12:46:52 -05:00
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . AppArmor , enabled ) ( )
2019-01-07 15:10:11 -05:00
DropDisabledPodFields ( newPod , oldPod )
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
2019-01-07 15:10:11 -05:00
}
switch {
case enabled || oldPodHasAppArmor :
// new pod should not be changed if the feature is enabled, or if the old pod had AppArmor
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2019-01-07 15:10:11 -05:00
}
case newPodHasAppArmor :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have AppArmor
if ! reflect . DeepEqual ( newPod , podWithoutAppArmor ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod had EmptyDir SizeLimit: %v" , cmp . Diff ( newPod , podWithoutAppArmor ( ) ) )
2019-01-07 15:10:11 -05:00
}
default :
// new pod should not need to be changed
2019-01-08 15:21:49 -05:00
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2019-01-08 15:21:49 -05:00
}
}
2021-06-23 18:24:35 -04:00
} )
}
2018-11-20 08:05:19 -05:00
}
}
}
2019-06-21 13:42:53 -04:00
2021-03-09 13:20:17 -05:00
func TestDropProbeGracePeriod ( t * testing . T ) {
podWithProbeGracePeriod := func ( ) * api . Pod {
2021-06-23 18:24:35 -04:00
livenessGracePeriod := int64 ( 10 )
livenessProbe := api . Probe { TerminationGracePeriodSeconds : & livenessGracePeriod }
startupGracePeriod := int64 ( 10 )
startupProbe := api . Probe { TerminationGracePeriodSeconds : & startupGracePeriod }
2021-03-09 13:20:17 -05:00
return & api . Pod {
Spec : api . PodSpec {
2021-03-11 19:40:40 -05:00
RestartPolicy : api . RestartPolicyNever ,
2021-06-23 18:24:35 -04:00
Containers : [ ] api . Container { { Name : "container1" , Image : "testimage" , LivenessProbe : & livenessProbe , StartupProbe : & startupProbe } } ,
2021-03-09 13:20:17 -05:00
} ,
}
}
podWithoutProbeGracePeriod := func ( ) * api . Pod {
p := podWithProbeGracePeriod ( )
p . Spec . Containers [ 0 ] . LivenessProbe . TerminationGracePeriodSeconds = nil
p . Spec . Containers [ 0 ] . StartupProbe . TerminationGracePeriodSeconds = nil
return p
}
podInfo := [ ] struct {
description string
hasGracePeriod bool
pod func ( ) * api . Pod
} {
{
description : "has probe-level terminationGracePeriod" ,
hasGracePeriod : true ,
pod : podWithProbeGracePeriod ,
} ,
{
description : "does not have probe-level terminationGracePeriod" ,
hasGracePeriod : false ,
pod : podWithoutProbeGracePeriod ,
} ,
{
description : "only has liveness probe-level terminationGracePeriod" ,
hasGracePeriod : true ,
2021-03-11 19:40:40 -05:00
pod : func ( ) * api . Pod {
p := podWithProbeGracePeriod ( )
p . Spec . Containers [ 0 ] . StartupProbe . TerminationGracePeriodSeconds = nil
return p
} ,
2021-03-09 13:20:17 -05:00
} ,
{
description : "is nil" ,
hasGracePeriod : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
2021-06-23 18:24:35 -04:00
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasGracePeriod , oldPod := oldPodInfo . hasGracePeriod , oldPodInfo . pod ( )
newPodHasGracePeriod , newPod := newPodInfo . hasGracePeriod , newPodInfo . pod ( )
if newPod == nil {
continue
2021-03-09 13:20:17 -05:00
}
2021-06-23 18:24:35 -04:00
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . ProbeTerminationGracePeriod , enabled ) ( )
2021-03-09 13:20:17 -05:00
2021-06-23 18:24:35 -04:00
var oldPodSpec * api . PodSpec
if oldPod != nil {
oldPodSpec = & oldPod . Spec
2021-03-09 13:20:17 -05:00
}
2021-06-23 18:24:35 -04:00
dropDisabledFields ( & newPod . Spec , nil , oldPodSpec , nil )
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
2021-03-09 13:20:17 -05:00
}
2021-06-23 18:24:35 -04:00
switch {
case enabled || oldPodHasGracePeriod :
// new pod should not be changed if the feature is enabled, or if the old pod had terminationGracePeriod
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
}
case newPodHasGracePeriod :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have terminationGracePeriod
if ! reflect . DeepEqual ( newPod , podWithoutProbeGracePeriod ( ) ) {
t . Errorf ( "new pod had probe-level terminationGracePeriod: %v" , cmp . Diff ( newPod , podWithoutProbeGracePeriod ( ) ) )
}
default :
// new pod should not need to be changed
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
}
2021-03-09 13:20:17 -05:00
}
2021-06-23 18:24:35 -04:00
} )
}
2021-03-09 13:20:17 -05:00
}
}
}
2019-08-02 20:00:01 -04:00
func TestDropEphemeralContainers ( t * testing . T ) {
podWithEphemeralContainers := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
EphemeralContainers : [ ] api . EphemeralContainer { { EphemeralContainerCommon : api . EphemeralContainerCommon { Name : "container1" , Image : "testimage" } } } ,
} ,
}
}
podWithoutEphemeralContainers := func ( ) * api . Pod {
return & api . Pod {
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyNever ,
} ,
}
}
podInfo := [ ] struct {
description string
hasEphemeralContainers bool
pod func ( ) * api . Pod
} {
{
2021-02-12 04:44:54 -05:00
description : "has ephemeral containers" ,
2019-08-02 20:00:01 -04:00
hasEphemeralContainers : true ,
pod : podWithEphemeralContainers ,
} ,
{
2021-02-12 04:44:54 -05:00
description : "does not have ephemeral containers" ,
2019-08-02 20:00:01 -04:00
hasEphemeralContainers : false ,
pod : podWithoutEphemeralContainers ,
} ,
{
description : "is nil" ,
hasEphemeralContainers : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasEphemeralContainers , oldPod := oldPodInfo . hasEphemeralContainers , oldPodInfo . pod ( )
newPodHasEphemeralContainers , newPod := newPodInfo . hasEphemeralContainers , newPodInfo . pod ( )
if newPod == nil {
continue
}
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . EphemeralContainers , enabled ) ( )
var oldPodSpec * api . PodSpec
if oldPod != nil {
oldPodSpec = & oldPod . Spec
}
dropDisabledFields ( & newPod . Spec , nil , oldPodSpec , nil )
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
2019-08-02 20:00:01 -04:00
}
switch {
case enabled || oldPodHasEphemeralContainers :
// new pod should not be changed if the feature is enabled, or if the old pod had subpaths
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2019-08-02 20:00:01 -04:00
}
case newPodHasEphemeralContainers :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have subpaths
if ! reflect . DeepEqual ( newPod , podWithoutEphemeralContainers ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod had subpaths: %v" , cmp . Diff ( newPod , podWithoutEphemeralContainers ( ) ) )
2019-08-02 20:00:01 -04:00
}
default :
// new pod should not need to be changed
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
2021-03-17 05:35:21 -04:00
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
2019-08-02 20:00:01 -04:00
}
}
} )
}
}
}
}
2021-02-17 15:39:42 -05:00
func TestValidatePodDeletionCostOption ( t * testing . T ) {
testCases := [ ] struct {
name string
oldPodMeta * metav1 . ObjectMeta
featureEnabled bool
wantAllowInvalidPodDeletionCost bool
} {
{
name : "CreateFeatureEnabled" ,
featureEnabled : true ,
wantAllowInvalidPodDeletionCost : false ,
} ,
{
name : "CreateFeatureDisabled" ,
featureEnabled : false ,
wantAllowInvalidPodDeletionCost : true ,
} ,
{
name : "UpdateFeatureDisabled" ,
2021-04-08 20:44:54 -04:00
oldPodMeta : & metav1 . ObjectMeta { Annotations : map [ string ] string { api . PodDeletionCost : "100" } } ,
2021-02-17 15:39:42 -05:00
featureEnabled : false ,
wantAllowInvalidPodDeletionCost : true ,
} ,
{
name : "UpdateFeatureEnabledValidOldValue" ,
2021-04-08 20:44:54 -04:00
oldPodMeta : & metav1 . ObjectMeta { Annotations : map [ string ] string { api . PodDeletionCost : "100" } } ,
2021-02-17 15:39:42 -05:00
featureEnabled : true ,
wantAllowInvalidPodDeletionCost : false ,
} ,
{
name : "UpdateFeatureEnabledValidOldValue" ,
2021-04-08 20:44:54 -04:00
oldPodMeta : & metav1 . ObjectMeta { Annotations : map [ string ] string { api . PodDeletionCost : "invalid-value" } } ,
2021-02-17 15:39:42 -05:00
featureEnabled : true ,
wantAllowInvalidPodDeletionCost : true ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . PodDeletionCost , tc . featureEnabled ) ( )
// The new pod doesn't impact the outcome.
gotOptions := GetValidationOptionsFromPodSpecAndMeta ( nil , nil , nil , tc . oldPodMeta )
if tc . wantAllowInvalidPodDeletionCost != gotOptions . AllowInvalidPodDeletionCost {
t . Errorf ( "unexpected diff, want: %v, got: %v" , tc . wantAllowInvalidPodDeletionCost , gotOptions . AllowInvalidPodDeletionCost )
}
2021-01-26 14:28:35 -05:00
} )
}
}
2021-05-24 07:58:59 -04:00
func TestHaveSameExpandedDNSConfig ( t * testing . T ) {
testCases := [ ] struct {
desc string
podSpec * api . PodSpec
oldPodSpec * api . PodSpec
want bool
} {
{
desc : "nil DNSConfig" ,
podSpec : & api . PodSpec { } ,
oldPodSpec : & api . PodSpec { } ,
want : false ,
} ,
{
desc : "empty DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig { } ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig { } ,
} ,
want : false ,
} ,
{
desc : "same legacy DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
want : false ,
} ,
{
desc : "update legacy DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"baz.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
want : false ,
} ,
{
desc : "same expanded DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"7.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"7.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
want : true ,
} ,
{
desc : "update expanded DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"baz.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"7.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
want : false ,
} ,
{
desc : "update to legacy DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"baz.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"7.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
want : false ,
} ,
{
desc : "update to expanded DNSConfig" ,
podSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
"baz.expanded.com" ,
"8.expanded.com" ,
"9.expanded.com" ,
"10.expanded.com" ,
"11.expanded.com" ,
"12.expanded.com" ,
"13.expanded.com" ,
"14.expanded.com" ,
"15.expanded.com" ,
"16.expanded.com" ,
"17.expanded.com" ,
"18.expanded.com" ,
"19.expanded.com" ,
"20.expanded.com" ,
"21.expanded.com" ,
"22.expanded.com" ,
"23.expanded.com" ,
"24.expanded.com" ,
"25.expanded.com" ,
"26.expanded.com" ,
"27.expanded.com" ,
"28.expanded.com" ,
"29.expanded.com" ,
"30.expanded.com" ,
"31.expanded.com" ,
"32.expanded.com" ,
} ,
} ,
} ,
oldPodSpec : & api . PodSpec {
DNSConfig : & api . PodDNSConfig {
Searches : [ ] string {
"foo.com" ,
"bar.io" ,
"3.com" ,
"4.com" ,
"5.com" ,
"6.com" ,
} ,
} ,
} ,
want : false ,
} ,
}
for _ , tc := range testCases {
t . Run ( tc . desc , func ( t * testing . T ) {
got := haveSameExpandedDNSConfig ( tc . podSpec , tc . oldPodSpec )
if tc . want != got {
t . Errorf ( "unexpected diff, want: %v, got: %v" , tc . want , got )
}
} )
}
}
2021-01-26 14:28:35 -05:00
func TestDropDisabledPodAffinityTermFields ( t * testing . T ) {
testCases := [ ] struct {
name string
enabled bool
podSpec * api . PodSpec
oldPodSpec * api . PodSpec
wantPodSpec * api . PodSpec
} {
{
name : "nil affinity" ,
podSpec : & api . PodSpec { } ,
wantPodSpec : & api . PodSpec { } ,
} ,
{
name : "empty affinity" ,
podSpec : & api . PodSpec { Affinity : & api . Affinity { } } ,
wantPodSpec : & api . PodSpec { Affinity : & api . Affinity { } } ,
} ,
{
name : "NamespaceSelector cleared" ,
podSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
} } ,
oldPodSpec : & api . PodSpec { Affinity : & api . Affinity { } } ,
wantPodSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" } } ,
} ,
} ,
} } ,
} ,
{
name : "NamespaceSelector not cleared since old spec already sets it" ,
podSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
} } ,
oldPodSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
} ,
} } ,
wantPodSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
} } ,
} ,
{
name : "NamespaceSelector not cleared since feature is enabled" ,
enabled : true ,
podSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
} } ,
wantPodSpec : & api . PodSpec { Affinity : & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns1" } , TopologyKey : "region1" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns2" } , TopologyKey : "region2" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{ LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns3" } , TopologyKey : "region3" , NamespaceSelector : & metav1 . LabelSelector { } } ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{ PodAffinityTerm : api . PodAffinityTerm { LabelSelector : & metav1 . LabelSelector { } , Namespaces : [ ] string { "ns4" } , TopologyKey : "region4" , NamespaceSelector : & metav1 . LabelSelector { } } } ,
} ,
} ,
} } ,
} ,
}
2021-02-17 15:39:42 -05:00
2021-01-26 14:28:35 -05:00
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . PodAffinityNamespaceSelector , tc . enabled ) ( )
dropDisabledPodAffinityTermFields ( tc . podSpec , tc . oldPodSpec )
if diff := cmp . Diff ( tc . wantPodSpec , tc . podSpec ) ; diff != "" {
t . Errorf ( "unexpected pod spec (-want, +got):\n%s" , diff )
}
2021-02-17 15:39:42 -05:00
} )
}
}
2021-10-20 15:40:49 -04:00
func TestDropOSField ( t * testing . T ) {
podWithOSField := func ( ) * api . Pod {
osField := api . PodOS { Name : "linux" }
return & api . Pod {
Spec : api . PodSpec {
OS : & osField ,
} ,
}
}
podWithoutOSField := func ( ) * api . Pod { return & api . Pod { } }
podInfo := [ ] struct {
description string
hasPodOSField bool
pod func ( ) * api . Pod
} {
{
description : "has PodOS field" ,
hasPodOSField : true ,
pod : podWithOSField ,
} ,
{
description : "does not have PodOS field" ,
hasPodOSField : false ,
pod : podWithoutOSField ,
} ,
{
description : "is nil" ,
hasPodOSField : false ,
pod : func ( ) * api . Pod { return nil } ,
} ,
}
for _ , enabled := range [ ] bool { true , false } {
for _ , oldPodInfo := range podInfo {
for _ , newPodInfo := range podInfo {
oldPodHasOsField , oldPod := oldPodInfo . hasPodOSField , oldPodInfo . pod ( )
newPodHasOSField , newPod := newPodInfo . hasPodOSField , newPodInfo . pod ( )
if newPod == nil {
continue
}
t . Run ( fmt . Sprintf ( "feature enabled=%v, old pod %v, new pod %v" , enabled , oldPodInfo . description , newPodInfo . description ) , func ( t * testing . T ) {
defer featuregatetesting . SetFeatureGateDuringTest ( t , utilfeature . DefaultFeatureGate , features . IdentifyPodOS , enabled ) ( )
var oldPodSpec * api . PodSpec
if oldPod != nil {
oldPodSpec = & oldPod . Spec
}
dropDisabledFields ( & newPod . Spec , nil , oldPodSpec , nil )
// old pod should never be changed
if ! reflect . DeepEqual ( oldPod , oldPodInfo . pod ( ) ) {
t . Errorf ( "old pod changed: %v" , cmp . Diff ( oldPod , oldPodInfo . pod ( ) ) )
}
switch {
case enabled || oldPodHasOsField :
// new pod should not be changed if the feature is enabled, or if the old pod had subpaths
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
}
case newPodHasOSField :
// new pod should be changed
if reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod was not changed" )
}
// new pod should not have OSfield
if ! reflect . DeepEqual ( newPod , podWithoutOSField ( ) ) {
t . Errorf ( "new pod has OS field: %v" , cmp . Diff ( newPod , podWithoutOSField ( ) ) )
}
default :
// new pod should not need to be changed
if ! reflect . DeepEqual ( newPod , newPodInfo . pod ( ) ) {
t . Errorf ( "new pod changed: %v" , cmp . Diff ( newPod , newPodInfo . pod ( ) ) )
}
}
} )
}
}
}
}