2014-07-01 16:01:39 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2014 The Kubernetes Authors .
2014-07-01 16:01:39 -04:00
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 .
* /
2014-08-29 18:48:41 -04:00
package validation
2014-07-01 16:01:39 -04:00
import (
2017-05-30 14:57:06 -04:00
"math"
2015-11-05 18:38:46 -05:00
"reflect"
2014-07-01 16:01:39 -04:00
"strings"
"testing"
2014-07-01 18:14:25 -04:00
2017-06-22 14:24:23 -04:00
"k8s.io/api/core/v1"
2017-01-25 08:13:07 -05:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-11 09:09:48 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-01-27 15:42:17 -05:00
"k8s.io/apimachinery/pkg/util/intstr"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/util/sets"
2017-07-10 19:51:24 -04:00
"k8s.io/apimachinery/pkg/util/validation"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/util/validation/field"
2017-04-13 15:03:30 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/api"
2017-04-10 13:49:54 -04:00
"k8s.io/kubernetes/pkg/api/helper"
2017-05-23 13:29:19 -04:00
_ "k8s.io/kubernetes/pkg/api/testapi"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/capabilities"
2016-08-16 19:41:05 -04:00
"k8s.io/kubernetes/pkg/security/apparmor"
2014-07-01 16:01:39 -04:00
)
2016-12-01 23:32:41 -05:00
const (
2017-01-13 08:40:19 -05:00
dnsLabelErrMsg = "a DNS-1123 label must consist of"
dnsSubdomainLabelErrMsg = "a DNS-1123 subdomain"
2017-06-17 18:34:22 -04:00
envVarNameErrMsg = "a valid environment variable name must consist of"
2016-12-01 23:32:41 -05:00
)
2017-06-07 01:10:09 -04:00
func newHostPathType ( pathType string ) * api . HostPathType {
hostPathType := new ( api . HostPathType )
* hostPathType = api . HostPathType ( pathType )
return hostPathType
}
2015-03-23 14:18:11 -04:00
func testVolume ( name string , namespace string , spec api . PersistentVolumeSpec ) * api . PersistentVolume {
2017-01-16 22:38:19 -05:00
objMeta := metav1 . ObjectMeta { Name : name }
2015-03-23 14:18:11 -04:00
if namespace != "" {
objMeta . Namespace = namespace
}
return & api . PersistentVolume {
ObjectMeta : objMeta ,
Spec : spec ,
}
}
2017-04-18 01:24:24 -04:00
func testVolumeWithNodeAffinity ( t * testing . T , name string , namespace string , affinity * api . NodeAffinity , spec api . PersistentVolumeSpec ) * api . PersistentVolume {
objMeta := metav1 . ObjectMeta { Name : name }
if namespace != "" {
objMeta . Namespace = namespace
}
objMeta . Annotations = map [ string ] string { }
err := helper . StorageNodeAffinityToAlphaAnnotation ( objMeta . Annotations , affinity )
if err != nil {
t . Fatalf ( "Failed to get node affinity annotation: %v" , err )
}
return & api . PersistentVolume {
ObjectMeta : objMeta ,
Spec : spec ,
}
}
2015-03-23 14:18:11 -04:00
func TestValidatePersistentVolumes ( t * testing . T ) {
scenarios := map [ string ] struct {
isExpectedFailure bool
volume * api . PersistentVolume
} {
"good-volume" : {
isExpectedFailure : false ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
2015-03-23 14:18:11 -04:00
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-03-23 14:18:11 -04:00
} ,
2017-03-02 04:23:57 -05:00
StorageClassName : "valid" ,
2015-03-23 14:18:11 -04:00
} ) ,
} ,
2016-09-01 15:12:13 -04:00
"good-volume-with-retain-policy" : {
isExpectedFailure : false ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2016-09-01 15:12:13 -04:00
} ,
PersistentVolumeReclaimPolicy : api . PersistentVolumeReclaimRetain ,
} ) ,
} ,
2015-07-24 15:55:54 -04:00
"invalid-accessmode" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { "fakemode" } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-07-24 15:55:54 -04:00
} ,
} ) ,
} ,
2016-09-01 15:12:13 -04:00
"invalid-reclaimpolicy" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2016-09-01 15:12:13 -04:00
} ,
PersistentVolumeReclaimPolicy : "fakeReclaimPolicy" ,
} ) ,
} ,
2015-03-23 14:18:11 -04:00
"unexpected-namespace" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "unexpected-namespace" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
2015-03-23 14:18:11 -04:00
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-03-23 14:18:11 -04:00
} ,
} ) ,
} ,
"bad-name" : {
isExpectedFailure : true ,
volume : testVolume ( "123*Bad(Name" , "unexpected-namespace" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
2015-03-23 14:18:11 -04:00
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-03-23 14:18:11 -04:00
} ,
2015-05-12 20:44:29 -04:00
} ) ,
2015-03-23 14:18:11 -04:00
} ,
"missing-name" : {
isExpectedFailure : true ,
volume : testVolume ( "" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
2015-03-23 14:18:11 -04:00
} ) ,
} ,
"missing-capacity" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec { } ) ,
} ,
2015-05-12 20:44:29 -04:00
"missing-accessmodes" : {
isExpectedFailure : true ,
volume : testVolume ( "goodname" , "missing-accessmodes" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-05-12 20:44:29 -04:00
} ,
} ) ,
} ,
2015-03-23 14:18:11 -04:00
"too-many-sources" : {
isExpectedFailure : true ,
volume : testVolume ( "" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "5G" ) ,
} ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2015-03-23 14:18:11 -04:00
GCEPersistentDisk : & api . GCEPersistentDiskVolumeSource { PDName : "foo" , FSType : "ext4" } ,
} ,
} ) ,
} ,
2016-08-22 14:45:46 -04:00
"host mount of / with recycle reclaim policy" : {
isExpectedFailure : true ,
volume : testVolume ( "bad-recycle-do-not-want" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2016-08-22 14:45:46 -04:00
} ,
PersistentVolumeReclaimPolicy : api . PersistentVolumeReclaimRecycle ,
} ) ,
} ,
"host mount of / with recycle reclaim policy 2" : {
isExpectedFailure : true ,
volume : testVolume ( "bad-recycle-do-not-want" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/a/.." ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2016-08-22 14:45:46 -04:00
} ,
PersistentVolumeReclaimPolicy : api . PersistentVolumeReclaimRecycle ,
} ) ,
} ,
2017-03-02 04:23:57 -05:00
"invalid-storage-class-name" : {
isExpectedFailure : true ,
volume : testVolume ( "invalid-storage-class-name" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2017-03-02 04:23:57 -05:00
} ,
StorageClassName : "-invalid-" ,
} ) ,
} ,
2017-04-18 01:24:24 -04:00
// LocalVolume alpha feature disabled
// TODO: remove when no longer alpha
"alpha disabled valid local volume" : {
isExpectedFailure : true ,
volume : testVolumeWithNodeAffinity (
t ,
"valid-local-volume" ,
"" ,
& api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "test-label-key" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "test-label-value" } ,
} ,
} ,
} ,
} ,
} ,
} ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo" ,
} ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
2017-06-10 09:48:42 -04:00
"bad-hostpath-volume-backsteps" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
2017-06-07 01:10:09 -04:00
HostPath : & api . HostPathVolumeSource {
Path : "/foo/.." ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
2017-06-10 09:48:42 -04:00
} ,
StorageClassName : "backstep-hostpath" ,
} ) ,
} ,
2017-06-17 00:57:01 -04:00
"bad-local-volume-backsteps" : {
isExpectedFailure : true ,
volume : testVolume ( "foo" , "" , api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo/.." ,
} ,
} ,
StorageClassName : "backstep-local" ,
} ) ,
} ,
2015-03-23 14:18:11 -04:00
}
for name , scenario := range scenarios {
errs := ValidatePersistentVolume ( scenario . volume )
if len ( errs ) == 0 && scenario . isExpectedFailure {
t . Errorf ( "Unexpected success for scenario: %s" , name )
}
if len ( errs ) > 0 && ! scenario . isExpectedFailure {
t . Errorf ( "Unexpected failure for scenario: %s - %+v" , name , errs )
}
}
}
2017-04-18 01:24:24 -04:00
func TestValidateLocalVolumes ( t * testing . T ) {
scenarios := map [ string ] struct {
isExpectedFailure bool
volume * api . PersistentVolume
} {
"valid local volume" : {
isExpectedFailure : false ,
volume : testVolumeWithNodeAffinity (
t ,
"valid-local-volume" ,
"" ,
& api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "test-label-key" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "test-label-value" } ,
} ,
} ,
} ,
} ,
} ,
} ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo" ,
} ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
"invalid local volume nil annotations" : {
isExpectedFailure : true ,
volume : testVolume (
"invalid-local-volume-nil-annotations" ,
"" ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo" ,
} ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
"invalid local volume empty affinity" : {
isExpectedFailure : true ,
volume : testVolumeWithNodeAffinity (
t ,
"invalid-local-volume-empty-affinity" ,
"" ,
& api . NodeAffinity { } ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo" ,
} ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
"invalid local volume preferred affinity" : {
isExpectedFailure : true ,
volume : testVolumeWithNodeAffinity (
t ,
"invalid-local-volume-preferred-affinity" ,
"" ,
& api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "test-label-key" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "test-label-value" } ,
} ,
} ,
} ,
} ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . PreferredSchedulingTerm {
{
Weight : 10 ,
Preference : api . NodeSelectorTerm {
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "test-label-key" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "test-label-value" } ,
} ,
} ,
} ,
} ,
} ,
} ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource {
Path : "/foo" ,
} ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
"invalid local volume empty path" : {
isExpectedFailure : true ,
volume : testVolumeWithNodeAffinity (
t ,
"invalid-local-volume-empty-path" ,
"" ,
& api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "test-label-key" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "test-label-value" } ,
} ,
} ,
} ,
} ,
} ,
} ,
api . PersistentVolumeSpec {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode { api . ReadWriteOnce } ,
PersistentVolumeSource : api . PersistentVolumeSource {
Local : & api . LocalVolumeSource { } ,
} ,
StorageClassName : "test-storage-class" ,
} ) ,
} ,
}
err := utilfeature . DefaultFeatureGate . Set ( "PersistentLocalVolumes=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalPersistentVolumes: %v" , err )
return
}
for name , scenario := range scenarios {
errs := ValidatePersistentVolume ( scenario . volume )
if len ( errs ) == 0 && scenario . isExpectedFailure {
t . Errorf ( "Unexpected success for scenario: %s" , name )
}
if len ( errs ) > 0 && ! scenario . isExpectedFailure {
t . Errorf ( "Unexpected failure for scenario: %s - %+v" , name , errs )
}
}
}
2015-03-23 14:18:11 -04:00
func testVolumeClaim ( name string , namespace string , spec api . PersistentVolumeClaimSpec ) * api . PersistentVolumeClaim {
return & api . PersistentVolumeClaim {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : name , Namespace : namespace } ,
2015-03-23 14:18:11 -04:00
Spec : spec ,
}
}
2016-11-04 14:06:34 -04:00
func testVolumeClaimStorageClass ( name string , namespace string , annval string , spec api . PersistentVolumeClaimSpec ) * api . PersistentVolumeClaim {
annotations := map [ string ] string {
2017-03-02 04:23:55 -05:00
v1 . BetaStorageClassAnnotation : annval ,
2016-11-04 14:06:34 -04:00
}
return & api . PersistentVolumeClaim {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-11-04 14:06:34 -04:00
Name : name ,
Namespace : namespace ,
Annotations : annotations ,
} ,
Spec : spec ,
}
}
func testVolumeClaimAnnotation ( name string , namespace string , ann string , annval string , spec api . PersistentVolumeClaimSpec ) * api . PersistentVolumeClaim {
annotations := map [ string ] string {
ann : annval ,
}
return & api . PersistentVolumeClaim {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-11-04 14:06:34 -04:00
Name : name ,
Namespace : namespace ,
Annotations : annotations ,
} ,
Spec : spec ,
}
}
2015-03-23 14:18:11 -04:00
func TestValidatePersistentVolumeClaim ( t * testing . T ) {
2017-03-02 04:23:57 -05:00
invalidClassName := "-invalid-"
validClassName := "valid"
2015-03-23 14:18:11 -04:00
scenarios := map [ string ] struct {
isExpectedFailure bool
claim * api . PersistentVolumeClaim
} {
"good-claim" : {
isExpectedFailure : false ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
2016-12-03 13:57:26 -05:00
Selector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
2016-05-06 23:18:54 -04:00
{
Key : "key2" ,
Operator : "Exists" ,
} ,
} ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
2017-03-02 04:23:57 -05:00
StorageClassName : & validClassName ,
2016-05-06 23:18:54 -04:00
} ) ,
} ,
"invalid-label-selector" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
2016-12-03 13:57:26 -05:00
Selector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
2016-05-06 23:18:54 -04:00
{
Key : "key2" ,
Operator : "InvalidOp" ,
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
} ,
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode {
2015-03-23 14:18:11 -04:00
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
2015-07-24 15:55:54 -04:00
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ) ,
} ,
"invalid-accessmode" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode { "fakemode" } ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
2015-03-23 14:18:11 -04:00
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ) ,
} ,
"missing-namespace" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "" , api . PersistentVolumeClaimSpec {
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode {
2015-03-23 14:18:11 -04:00
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ) ,
} ,
"no-access-modes" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ) ,
} ,
"no-resource-requests" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode {
2015-03-23 14:18:11 -04:00
api . ReadWriteOnce ,
} ,
} ) ,
} ,
2015-03-31 11:37:09 -04:00
"invalid-resource-requests" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
2015-05-18 16:22:30 -04:00
AccessModes : [ ] api . PersistentVolumeAccessMode {
2015-03-31 11:37:09 -04:00
api . ReadWriteOnce ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ) ,
} ,
2016-08-10 14:18:37 -04:00
"negative-storage-request" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
2016-12-03 13:57:26 -05:00
Selector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
2016-08-10 14:18:37 -04:00
{
Key : "key2" ,
Operator : "Exists" ,
} ,
} ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "-10G" ) ,
} ,
} ,
} ) ,
} ,
2017-03-02 04:23:57 -05:00
"invalid-storage-class-name" : {
isExpectedFailure : true ,
claim : testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
Selector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : "Exists" ,
} ,
} ,
} ,
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
StorageClassName : & invalidClassName ,
} ) ,
} ,
2015-03-23 14:18:11 -04:00
}
for name , scenario := range scenarios {
errs := ValidatePersistentVolumeClaim ( scenario . claim )
if len ( errs ) == 0 && scenario . isExpectedFailure {
t . Errorf ( "Unexpected success for scenario: %s" , name )
}
if len ( errs ) > 0 && ! scenario . isExpectedFailure {
t . Errorf ( "Unexpected failure for scenario: %s - %+v" , name , errs )
}
}
}
2016-02-09 15:28:37 -05:00
func TestValidatePersistentVolumeClaimUpdate ( t * testing . T ) {
validClaim := testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} )
2016-11-04 14:06:34 -04:00
validClaimStorageClass := testVolumeClaimStorageClass ( "foo" , "ns" , "fast" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} )
validClaimAnnotation := testVolumeClaimAnnotation ( "foo" , "ns" , "description" , "foo-description" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} )
2016-02-09 15:28:37 -05:00
validUpdateClaim := testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
invalidUpdateClaimResources := testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "20G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
invalidUpdateClaimAccessModes := testVolumeClaim ( "foo" , "ns" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
2016-11-04 14:06:34 -04:00
invalidUpdateClaimStorageClass := testVolumeClaimStorageClass ( "foo" , "ns" , "fast2" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
validUpdateClaimMutableAnnotation := testVolumeClaimAnnotation ( "foo" , "ns" , "description" , "updated-or-added-foo-description" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
validAddClaimAnnotation := testVolumeClaimAnnotation ( "foo" , "ns" , "description" , "updated-or-added-foo-description" , api . PersistentVolumeClaimSpec {
AccessModes : [ ] api . PersistentVolumeAccessMode {
api . ReadWriteOnce ,
api . ReadOnlyMany ,
} ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceStorage ) : resource . MustParse ( "10G" ) ,
} ,
} ,
VolumeName : "volume" ,
} )
2016-02-09 15:28:37 -05:00
scenarios := map [ string ] struct {
isExpectedFailure bool
oldClaim * api . PersistentVolumeClaim
newClaim * api . PersistentVolumeClaim
} {
2016-07-08 12:08:15 -04:00
"valid-update-volumeName-only" : {
2016-02-09 15:28:37 -05:00
isExpectedFailure : false ,
oldClaim : validClaim ,
newClaim : validUpdateClaim ,
} ,
2016-07-08 12:08:15 -04:00
"valid-no-op-update" : {
isExpectedFailure : false ,
oldClaim : validUpdateClaim ,
newClaim : validUpdateClaim ,
} ,
2016-02-09 15:28:37 -05:00
"invalid-update-change-resources-on-bound-claim" : {
isExpectedFailure : true ,
oldClaim : validUpdateClaim ,
newClaim : invalidUpdateClaimResources ,
} ,
"invalid-update-change-access-modes-on-bound-claim" : {
isExpectedFailure : true ,
oldClaim : validUpdateClaim ,
newClaim : invalidUpdateClaimAccessModes ,
} ,
2016-11-04 14:06:34 -04:00
"invalid-update-change-storage-class-annotation-after-creation" : {
isExpectedFailure : true ,
oldClaim : validClaimStorageClass ,
newClaim : invalidUpdateClaimStorageClass ,
} ,
"valid-update-mutable-annotation" : {
isExpectedFailure : false ,
oldClaim : validClaimAnnotation ,
newClaim : validUpdateClaimMutableAnnotation ,
} ,
"valid-update-add-annotation" : {
isExpectedFailure : false ,
oldClaim : validClaim ,
newClaim : validAddClaimAnnotation ,
} ,
2016-02-09 15:28:37 -05:00
}
for name , scenario := range scenarios {
// ensure we have a resource version specified for updates
scenario . oldClaim . ResourceVersion = "1"
scenario . newClaim . ResourceVersion = "1"
errs := ValidatePersistentVolumeClaimUpdate ( scenario . newClaim , scenario . oldClaim )
if len ( errs ) == 0 && scenario . isExpectedFailure {
t . Errorf ( "Unexpected success for scenario: %s" , name )
}
if len ( errs ) > 0 && ! scenario . isExpectedFailure {
t . Errorf ( "Unexpected failure for scenario: %s - %+v" , name , errs )
}
}
}
2016-07-30 01:41:20 -04:00
func TestValidateKeyToPath ( t * testing . T ) {
testCases := [ ] struct {
kp api . KeyToPath
ok bool
errtype field . ErrorType
} {
{
kp : api . KeyToPath { Key : "k" , Path : "p" } ,
ok : true ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "p/p/p/p" } ,
ok : true ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "p/..p/p../p..p" } ,
ok : true ,
} ,
2016-07-10 20:48:28 -04:00
{
kp : api . KeyToPath { Key : "k" , Path : "p" , Mode : newInt32 ( 0644 ) } ,
ok : true ,
} ,
2016-07-30 01:41:20 -04:00
{
kp : api . KeyToPath { Key : "" , Path : "p" } ,
ok : false ,
errtype : field . ErrorTypeRequired ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "" } ,
ok : false ,
errtype : field . ErrorTypeRequired ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "..p" } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "../p" } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "p/../p" } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "p/.." } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
2016-07-10 20:48:28 -04:00
{
kp : api . KeyToPath { Key : "k" , Path : "p" , Mode : newInt32 ( 01000 ) } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
{
kp : api . KeyToPath { Key : "k" , Path : "p" , Mode : newInt32 ( - 1 ) } ,
ok : false ,
errtype : field . ErrorTypeInvalid ,
} ,
2014-07-01 17:40:36 -04:00
}
2016-07-30 01:41:20 -04:00
for i , tc := range testCases {
errs := validateKeyToPath ( & tc . kp , field . NewPath ( "field" ) )
if tc . ok && len ( errs ) > 0 {
t . Errorf ( "[%d] unexpected errors: %v" , i , errs )
} else if ! tc . ok && len ( errs ) == 0 {
t . Errorf ( "[%d] expected error type %v" , i , tc . errtype )
} else if len ( errs ) > 1 {
t . Errorf ( "[%d] expected only one error, got %d" , i , len ( errs ) )
} else if ! tc . ok {
if errs [ 0 ] . Type != tc . errtype {
t . Errorf ( "[%d] expected error type %v, got %v" , i , tc . errtype , errs [ 0 ] . Type )
}
}
2014-07-01 17:40:36 -04:00
}
2016-07-30 01:41:20 -04:00
}
2017-05-27 10:37:56 -04:00
func TestValidateNFSVolumeSource ( t * testing . T ) {
testCases := [ ] struct {
name string
nfs * api . NFSVolumeSource
errtype field . ErrorType
errfield string
errdetail string
} {
{
name : "missing server" ,
nfs : & api . NFSVolumeSource { Server : "" , Path : "/tmp" } ,
errtype : field . ErrorTypeRequired ,
errfield : "server" ,
} ,
{
name : "missing path" ,
nfs : & api . NFSVolumeSource { Server : "my-server" , Path : "" } ,
errtype : field . ErrorTypeRequired ,
errfield : "path" ,
} ,
{
name : "abs path" ,
nfs : & api . NFSVolumeSource { Server : "my-server" , Path : "tmp" } ,
errtype : field . ErrorTypeInvalid ,
errfield : "path" ,
errdetail : "must be an absolute path" ,
} ,
}
for i , tc := range testCases {
errs := validateNFSVolumeSource ( tc . nfs , field . NewPath ( "field" ) )
if len ( errs ) > 0 && tc . errtype == "" {
t . Errorf ( "[%d: %q] unexpected error(s): %v" , i , tc . name , errs )
} else if len ( errs ) == 0 && tc . errtype != "" {
t . Errorf ( "[%d: %q] expected error type %v" , i , tc . name , tc . errtype )
} else if len ( errs ) >= 1 {
if errs [ 0 ] . Type != tc . errtype {
t . Errorf ( "[%d: %q] expected error type %v, got %v" , i , tc . name , tc . errtype , errs [ 0 ] . Type )
} else if ! strings . HasSuffix ( errs [ 0 ] . Field , "." + tc . errfield ) {
t . Errorf ( "[%d: %q] expected error on field %q, got %q" , i , tc . name , tc . errfield , errs [ 0 ] . Field )
} else if ! strings . Contains ( errs [ 0 ] . Detail , tc . errdetail ) {
t . Errorf ( "[%d: %q] expected error detail %q, got %q" , i , tc . name , tc . errdetail , errs [ 0 ] . Detail )
}
}
}
}
func TestValidateGlusterfs ( t * testing . T ) {
testCases := [ ] struct {
name string
gfs * api . GlusterfsVolumeSource
errtype field . ErrorType
errfield string
} {
{
name : "missing endpointname" ,
gfs : & api . GlusterfsVolumeSource { EndpointsName : "" , Path : "/tmp" } ,
errtype : field . ErrorTypeRequired ,
errfield : "endpoints" ,
} ,
{
name : "missing path" ,
gfs : & api . GlusterfsVolumeSource { EndpointsName : "my-endpoint" , Path : "" } ,
errtype : field . ErrorTypeRequired ,
errfield : "path" ,
} ,
{
name : "missing endpintname and path" ,
gfs : & api . GlusterfsVolumeSource { EndpointsName : "" , Path : "" } ,
errtype : field . ErrorTypeRequired ,
errfield : "endpoints" ,
} ,
}
for i , tc := range testCases {
2017-08-22 05:11:25 -04:00
errs := validateGlusterfsVolumeSource ( tc . gfs , field . NewPath ( "field" ) )
2017-05-27 10:37:56 -04:00
if len ( errs ) > 0 && tc . errtype == "" {
t . Errorf ( "[%d: %q] unexpected error(s): %v" , i , tc . name , errs )
} else if len ( errs ) == 0 && tc . errtype != "" {
t . Errorf ( "[%d: %q] expected error type %v" , i , tc . name , tc . errtype )
} else if len ( errs ) >= 1 {
if errs [ 0 ] . Type != tc . errtype {
t . Errorf ( "[%d: %q] expected error type %v, got %v" , i , tc . name , tc . errtype , errs [ 0 ] . Type )
} else if ! strings . HasSuffix ( errs [ 0 ] . Field , "." + tc . errfield ) {
t . Errorf ( "[%d: %q] expected error on field %q, got %q" , i , tc . name , tc . errfield , errs [ 0 ] . Field )
}
}
}
}
2016-07-30 02:52:25 -04:00
// helper
func newInt32 ( val int ) * int32 {
p := new ( int32 )
* p = int32 ( val )
return p
}
// This test is a little too top-to-bottom. Ideally we would test each volume
// type on its own, but we want to also make sure that the logic works through
// the one-of wrapper, so we just do it all in one place.
2014-07-01 17:40:36 -04:00
func TestValidateVolumes ( t * testing . T ) {
2017-07-11 23:37:48 -04:00
validInitiatorName := "iqn.2015-02.example.com:init"
invalidInitiatorName := "2015-02.example.com:init"
2016-07-30 02:52:25 -04:00
testCases := [ ] struct {
name string
vol api . Volume
errtype field . ErrorType
errfield string
errdetail string
2014-08-19 23:54:20 -04:00
} {
2016-07-30 02:52:25 -04:00
// EmptyDir and basic volume names
{
name : "valid alpha name" ,
vol : api . Volume {
Name : "empty" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource { } ,
} ,
} ,
} ,
{
name : "valid num name" ,
vol : api . Volume {
Name : "123" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource { } ,
} ,
} ,
} ,
{
name : "valid alphanum name" ,
vol : api . Volume {
Name : "empty-123" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource { } ,
} ,
} ,
} ,
{
name : "valid numalpha name" ,
vol : api . Volume {
Name : "123-empty" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource { } ,
} ,
} ,
} ,
{
name : "zero-length name" ,
vol : api . Volume {
Name : "" ,
VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "name" ,
} ,
{
name : "name > 63 characters" ,
vol : api . Volume {
Name : strings . Repeat ( "a" , 64 ) ,
VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "name" ,
errdetail : "must be no more than" ,
} ,
{
name : "name not a DNS label" ,
vol : api . Volume {
Name : "a.b.c" ,
VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "name" ,
2016-12-01 23:32:41 -05:00
errdetail : dnsLabelErrMsg ,
2016-07-30 02:52:25 -04:00
} ,
// More than one source field specified.
{
name : "more than one source" ,
vol : api . Volume {
Name : "dups" ,
VolumeSource : api . VolumeSource {
EmptyDir : & api . EmptyDirVolumeSource { } ,
HostPath : & api . HostPathVolumeSource {
Path : "/mnt/path" ,
2017-06-07 01:10:09 -04:00
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
2016-07-30 02:52:25 -04:00
} ,
} ,
} ,
errtype : field . ErrorTypeForbidden ,
errfield : "hostPath" ,
errdetail : "may not specify more than 1 volume" ,
} ,
2017-06-07 01:10:09 -04:00
// HostPath Default
{
name : "default HostPath" ,
vol : api . Volume {
Name : "hostpath" ,
VolumeSource : api . VolumeSource {
HostPath : & api . HostPathVolumeSource {
Path : "/mnt/path" ,
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
} ,
} ,
} ,
} ,
// HostPath Supported
2016-07-30 02:52:25 -04:00
{
name : "valid HostPath" ,
vol : api . Volume {
Name : "hostpath" ,
VolumeSource : api . VolumeSource {
HostPath : & api . HostPathVolumeSource {
Path : "/mnt/path" ,
2017-06-07 01:10:09 -04:00
Type : newHostPathType ( string ( api . HostPathSocket ) ) ,
} ,
} ,
} ,
} ,
// HostPath Invalid
{
name : "invalid HostPath" ,
vol : api . Volume {
Name : "hostpath" ,
VolumeSource : api . VolumeSource {
HostPath : & api . HostPathVolumeSource {
Path : "/mnt/path" ,
Type : newHostPathType ( "invalid" ) ,
2016-07-30 02:52:25 -04:00
} ,
} ,
} ,
2017-06-07 01:10:09 -04:00
errtype : field . ErrorTypeNotSupported ,
errfield : "type" ,
2016-07-30 02:52:25 -04:00
} ,
2017-06-10 09:48:42 -04:00
{
name : "invalid HostPath backsteps" ,
vol : api . Volume {
Name : "hostpath" ,
VolumeSource : api . VolumeSource {
HostPath : & api . HostPathVolumeSource {
Path : "/mnt/path/.." ,
2017-06-07 01:10:09 -04:00
Type : newHostPathType ( string ( api . HostPathDirectory ) ) ,
2017-06-10 09:48:42 -04:00
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "path" ,
errdetail : "must not contain '..'" ,
} ,
2016-07-30 02:52:25 -04:00
// GcePersistentDisk
{
name : "valid GcePersistentDisk" ,
vol : api . Volume {
Name : "gce-pd" ,
VolumeSource : api . VolumeSource {
GCEPersistentDisk : & api . GCEPersistentDiskVolumeSource {
PDName : "my-PD" ,
FSType : "ext4" ,
Partition : 1 ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
// AWSElasticBlockStore
{
name : "valid AWSElasticBlockStore" ,
vol : api . Volume {
Name : "aws-ebs" ,
VolumeSource : api . VolumeSource {
AWSElasticBlockStore : & api . AWSElasticBlockStoreVolumeSource {
VolumeID : "my-PD" ,
FSType : "ext4" ,
Partition : 1 ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
// GitRepo
{
name : "valid GitRepo" ,
vol : api . Volume {
Name : "git-repo" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "my-repo" ,
Revision : "hashstring" ,
Directory : "target" ,
} ,
} ,
} ,
} ,
{
name : "valid GitRepo in ." ,
vol : api . Volume {
Name : "git-repo-dot" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "my-repo" ,
Directory : "." ,
} ,
} ,
} ,
} ,
{
name : "valid GitRepo with .. in name" ,
vol : api . Volume {
Name : "git-repo-dot-dot-foo" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "my-repo" ,
Directory : "..foo" ,
} ,
} ,
} ,
} ,
{
name : "GitRepo starts with ../" ,
vol : api . Volume {
Name : "gitrepo" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "foo" ,
Directory : "../dots/bar" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "gitRepo.directory" ,
errdetail : ` must not contain '..' ` ,
} ,
{
name : "GitRepo contains .." ,
vol : api . Volume {
Name : "gitrepo" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "foo" ,
Directory : "dots/../bar" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "gitRepo.directory" ,
errdetail : ` must not contain '..' ` ,
} ,
{
name : "GitRepo absolute target" ,
vol : api . Volume {
Name : "gitrepo" ,
VolumeSource : api . VolumeSource {
GitRepo : & api . GitRepoVolumeSource {
Repository : "foo" ,
Directory : "/abstarget" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "gitRepo.directory" ,
} ,
// ISCSI
{
name : "valid ISCSI" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
2017-07-11 23:37:48 -04:00
{
name : "valid IQN: eui format" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "eui.0123456789ABCDEF" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
name : "valid IQN: naa format" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "naa.62004567BA64678D0123456789ABCDEF" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
2016-07-30 02:52:25 -04:00
{
name : "empty portal" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "iscsi.targetPortal" ,
} ,
{
name : "empty iqn" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "iscsi.iqn" ,
} ,
2017-07-11 23:37:48 -04:00
{
name : "invalid IQN: iqn format" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test;ls;" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "iscsi.iqn" ,
} ,
{
name : "invalid IQN: eui format" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "eui.0123456789ABCDEFGHIJ" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "iscsi.iqn" ,
} ,
{
name : "invalid IQN: naa format" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "naa.62004567BA_4-78D.123456789ABCDEF" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "iscsi.iqn" ,
} ,
{
name : "valid initiatorName" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
InitiatorName : & validInitiatorName ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
name : "invalid initiatorName" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
InitiatorName : & invalidInitiatorName ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "iscsi.initiatorname" ,
} ,
2017-03-17 15:50:39 -04:00
{
name : "empty secret" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
DiscoveryCHAPAuth : true ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "iscsi.secretRef" ,
} ,
{
name : "empty secret" ,
vol : api . Volume {
Name : "iscsi" ,
VolumeSource : api . VolumeSource {
ISCSI : & api . ISCSIVolumeSource {
TargetPortal : "127.0.0.1" ,
IQN : "iqn.2015-02.example.com:test" ,
Lun : 1 ,
FSType : "ext4" ,
ReadOnly : false ,
SessionCHAPAuth : true ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "iscsi.secretRef" ,
} ,
2016-07-30 02:52:25 -04:00
// Secret
{
name : "valid Secret" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "my-secret" ,
} ,
} ,
} ,
} ,
{
2016-07-10 20:48:28 -04:00
name : "valid Secret with defaultMode" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "my-secret" ,
DefaultMode : newInt32 ( 0644 ) ,
} ,
} ,
} ,
} ,
{
name : "valid Secret with projection and mode" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "my-secret" ,
Items : [ ] api . KeyToPath { {
Key : "key" ,
Path : "filename" ,
2016-07-10 20:48:28 -04:00
Mode : newInt32 ( 0644 ) ,
2016-07-30 02:52:25 -04:00
} } ,
} ,
} ,
} ,
} ,
{
name : "valid Secret with subdir projection" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "my-secret" ,
Items : [ ] api . KeyToPath { {
Key : "key" ,
Path : "dir/filename" ,
} } ,
} ,
} ,
} ,
} ,
{
name : "secret with missing path" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "s" ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "secret.items[0].path" ,
} ,
{
name : "secret with leading .." ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "s" ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "../foo" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "secret.items[0].path" ,
} ,
{
name : "secret with .. inside" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "s" ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "foo/../bar" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "secret.items[0].path" ,
} ,
2016-07-10 20:48:28 -04:00
{
name : "secret with invalid positive defaultMode" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "s" ,
DefaultMode : newInt32 ( 01000 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "secret.defaultMode" ,
} ,
{
name : "secret with invalid negative defaultMode" ,
vol : api . Volume {
Name : "secret" ,
VolumeSource : api . VolumeSource {
Secret : & api . SecretVolumeSource {
SecretName : "s" ,
DefaultMode : newInt32 ( - 1 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "secret.defaultMode" ,
} ,
2016-07-30 02:52:25 -04:00
// ConfigMap
{
name : "valid ConfigMap" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference {
Name : "my-cfgmap" ,
} ,
} ,
} ,
} ,
} ,
{
2016-07-10 20:48:28 -04:00
name : "valid ConfigMap with defaultMode" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference {
Name : "my-cfgmap" ,
} ,
DefaultMode : newInt32 ( 0644 ) ,
} ,
} ,
} ,
} ,
{
name : "valid ConfigMap with projection and mode" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference {
Name : "my-cfgmap" } ,
Items : [ ] api . KeyToPath { {
Key : "key" ,
Path : "filename" ,
2016-07-10 20:48:28 -04:00
Mode : newInt32 ( 0644 ) ,
2016-07-30 02:52:25 -04:00
} } ,
} ,
} ,
} ,
} ,
{
name : "valid ConfigMap with subdir projection" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference {
Name : "my-cfgmap" } ,
Items : [ ] api . KeyToPath { {
Key : "key" ,
Path : "dir/filename" ,
} } ,
} ,
} ,
} ,
} ,
{
name : "configmap with missing path" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference { Name : "c" } ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "configMap.items[0].path" ,
} ,
{
name : "configmap with leading .." ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference { Name : "c" } ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "../foo" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "configMap.items[0].path" ,
} ,
{
name : "configmap with .. inside" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference { Name : "c" } ,
Items : [ ] api . KeyToPath { { Key : "key" , Path : "foo/../bar" } } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "configMap.items[0].path" ,
} ,
2016-07-10 20:48:28 -04:00
{
name : "configmap with invalid positive defaultMode" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference { Name : "c" } ,
DefaultMode : newInt32 ( 01000 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "configMap.defaultMode" ,
} ,
{
name : "configmap with invalid negative defaultMode" ,
vol : api . Volume {
Name : "cfgmap" ,
VolumeSource : api . VolumeSource {
ConfigMap : & api . ConfigMapVolumeSource {
LocalObjectReference : api . LocalObjectReference { Name : "c" } ,
DefaultMode : newInt32 ( - 1 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "configMap.defaultMode" ,
} ,
2016-07-30 02:52:25 -04:00
// Glusterfs
{
name : "valid Glusterfs" ,
vol : api . Volume {
Name : "glusterfs" ,
VolumeSource : api . VolumeSource {
Glusterfs : & api . GlusterfsVolumeSource {
EndpointsName : "host1" ,
Path : "path" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
name : "empty hosts" ,
vol : api . Volume {
Name : "glusterfs" ,
VolumeSource : api . VolumeSource {
Glusterfs : & api . GlusterfsVolumeSource {
EndpointsName : "" ,
Path : "path" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "glusterfs.endpoints" ,
} ,
{
name : "empty path" ,
vol : api . Volume {
Name : "glusterfs" ,
VolumeSource : api . VolumeSource {
Glusterfs : & api . GlusterfsVolumeSource {
EndpointsName : "host" ,
Path : "" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "glusterfs.path" ,
} ,
// Flocker
{
2016-08-01 06:45:03 -04:00
name : "valid Flocker -- datasetUUID" ,
vol : api . Volume {
Name : "flocker" ,
VolumeSource : api . VolumeSource {
Flocker : & api . FlockerVolumeSource {
DatasetUUID : "d846b09d-223d-43df-ab5b-d6db2206a0e4" ,
} ,
} ,
} ,
} ,
{
name : "valid Flocker -- datasetName" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "flocker" ,
VolumeSource : api . VolumeSource {
Flocker : & api . FlockerVolumeSource {
DatasetName : "datasetName" ,
} ,
} ,
} ,
} ,
{
2016-08-01 06:45:03 -04:00
name : "both empty" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "flocker" ,
VolumeSource : api . VolumeSource {
Flocker : & api . FlockerVolumeSource {
DatasetName : "" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
2016-08-01 06:45:03 -04:00
errfield : "flocker" ,
} ,
{
name : "both specified" ,
vol : api . Volume {
Name : "flocker" ,
VolumeSource : api . VolumeSource {
Flocker : & api . FlockerVolumeSource {
DatasetName : "datasetName" ,
DatasetUUID : "d846b09d-223d-43df-ab5b-d6db2206a0e4" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "flocker" ,
2016-07-30 02:52:25 -04:00
} ,
{
name : "slash in flocker datasetName" ,
vol : api . Volume {
Name : "flocker" ,
VolumeSource : api . VolumeSource {
Flocker : & api . FlockerVolumeSource {
DatasetName : "foo/bar" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "flocker.datasetName" ,
errdetail : "must not contain '/'" ,
} ,
// RBD
{
name : "valid RBD" ,
vol : api . Volume {
Name : "rbd" ,
VolumeSource : api . VolumeSource {
RBD : & api . RBDVolumeSource {
CephMonitors : [ ] string { "foo" } ,
RBDImage : "bar" ,
FSType : "ext4" ,
} ,
} ,
} ,
} ,
{
name : "empty rbd monitors" ,
vol : api . Volume {
Name : "rbd" ,
VolumeSource : api . VolumeSource {
RBD : & api . RBDVolumeSource {
CephMonitors : [ ] string { } ,
RBDImage : "bar" ,
FSType : "ext4" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "rbd.monitors" ,
} ,
{
name : "empty image" ,
vol : api . Volume {
Name : "rbd" ,
VolumeSource : api . VolumeSource {
RBD : & api . RBDVolumeSource {
CephMonitors : [ ] string { "foo" } ,
RBDImage : "" ,
FSType : "ext4" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "rbd.image" ,
} ,
// Cinder
{
name : "valid Cinder" ,
vol : api . Volume {
Name : "cinder" ,
VolumeSource : api . VolumeSource {
Cinder : & api . CinderVolumeSource {
VolumeID : "29ea5088-4f60-4757-962e-dba678767887" ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
// CephFS
{
name : "valid CephFS" ,
vol : api . Volume {
Name : "cephfs" ,
VolumeSource : api . VolumeSource {
CephFS : & api . CephFSVolumeSource {
Monitors : [ ] string { "foo" } ,
} ,
} ,
} ,
} ,
{
name : "empty cephfs monitors" ,
vol : api . Volume {
Name : "cephfs" ,
VolumeSource : api . VolumeSource {
CephFS : & api . CephFSVolumeSource {
Monitors : [ ] string { } ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "cephfs.monitors" ,
} ,
// DownwardAPI
{
name : "valid DownwardAPI" ,
vol : api . Volume {
Name : "downwardapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile {
{
Path : "labels" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} ,
{
Path : "annotations" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.annotations" ,
} ,
} ,
{
Path : "namespace" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.namespace" ,
} ,
} ,
{
Path : "name" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.name" ,
} ,
} ,
{
Path : "path/with/subdirs" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} ,
{
Path : "path/./withdot" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} ,
{
Path : "path/with/embedded..dotdot" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} ,
{
Path : "path/with/leading/..dotdot" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} ,
{
Path : "cpu_limit" ,
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "limits.cpu" ,
} ,
} ,
{
Path : "cpu_request" ,
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "requests.cpu" ,
} ,
} ,
{
Path : "memory_limit" ,
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "limits.memory" ,
} ,
} ,
{
Path : "memory_request" ,
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "requests.memory" ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
2016-07-10 20:48:28 -04:00
{
name : "downapi valid defaultMode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
DefaultMode : newInt32 ( 0644 ) ,
} ,
} ,
} ,
} ,
{
name : "downapi valid item mode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Mode : newInt32 ( 0644 ) ,
Path : "path" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
} ,
{
name : "downapi invalid positive item mode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Mode : newInt32 ( 01000 ) ,
Path : "path" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.mode" ,
} ,
{
name : "downapi invalid negative item mode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Mode : newInt32 ( - 1 ) ,
Path : "path" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.mode" ,
} ,
2016-07-30 02:52:25 -04:00
{
name : "downapi empty metatada path" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "downwardAPI.path" ,
} ,
{
name : "downapi absolute path" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "/absolutepath" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.path" ,
} ,
{
name : "downapi dot dot path" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "../../passwd" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.path" ,
errdetail : ` must not contain '..' ` ,
} ,
{
name : "downapi dot dot file name" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "..badFileName" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.path" ,
errdetail : ` must not start with '..' ` ,
} ,
{
name : "downapi dot dot first level dirent" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "..badDirName/goodFileName" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.path" ,
errdetail : ` must not start with '..' ` ,
} ,
{
name : "downapi fieldRef and ResourceFieldRef together" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
Items : [ ] api . DownwardAPIVolumeFile { {
Path : "test" ,
FieldRef : & api . ObjectFieldSelector {
APIVersion : "v1" ,
FieldPath : "metadata.labels" ,
} ,
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "requests.memory" ,
} ,
} } ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI" ,
errdetail : "fieldRef and resourceFieldRef can not be specified simultaneously" ,
} ,
2016-07-10 20:48:28 -04:00
{
name : "downapi invalid positive defaultMode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
DefaultMode : newInt32 ( 01000 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.defaultMode" ,
} ,
{
name : "downapi invalid negative defaultMode" ,
vol : api . Volume {
Name : "downapi" ,
VolumeSource : api . VolumeSource {
DownwardAPI : & api . DownwardAPIVolumeSource {
DefaultMode : newInt32 ( - 1 ) ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "downwardAPI.defaultMode" ,
} ,
2016-07-30 02:52:25 -04:00
// FC
{
2017-07-10 19:51:24 -04:00
name : "FC valid targetWWNs and lun" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
TargetWWNs : [ ] string { "some_wwn" } ,
Lun : newInt32 ( 1 ) ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
2017-07-10 19:51:24 -04:00
name : "FC valid wwids" ,
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
WWIDs : [ ] string { "some_wwid" } ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
name : "FC empty targetWWNs and wwids" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
TargetWWNs : [ ] string { } ,
Lun : newInt32 ( 1 ) ,
2017-07-10 19:51:24 -04:00
WWIDs : [ ] string { } ,
2016-07-30 02:52:25 -04:00
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
2017-07-10 19:51:24 -04:00
errtype : field . ErrorTypeRequired ,
errfield : "fc.targetWWNs" ,
errdetail : "must specify either targetWWNs or wwids" ,
} ,
{
name : "FC invalid: both targetWWNs and wwids simultaneously" ,
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
TargetWWNs : [ ] string { "some_wwn" } ,
Lun : newInt32 ( 1 ) ,
WWIDs : [ ] string { "some_wwid" } ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "fc.targetWWNs" ,
errdetail : "targetWWNs and wwids can not be specified simultaneously" ,
2016-07-30 02:52:25 -04:00
} ,
{
2017-07-10 19:51:24 -04:00
name : "FC valid targetWWNs and empty lun" ,
2016-07-30 02:52:25 -04:00
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
TargetWWNs : [ ] string { "wwn" } ,
Lun : nil ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
2017-07-10 19:51:24 -04:00
errtype : field . ErrorTypeRequired ,
errfield : "fc.lun" ,
errdetail : "lun is required if targetWWNs is specified" ,
} ,
{
name : "FC valid targetWWNs and invalid lun" ,
vol : api . Volume {
Name : "fc" ,
VolumeSource : api . VolumeSource {
FC : & api . FCVolumeSource {
TargetWWNs : [ ] string { "wwn" } ,
Lun : newInt32 ( 256 ) ,
FSType : "ext4" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "fc.lun" ,
errdetail : validation . InclusiveRangeError ( 0 , 255 ) ,
2016-07-30 02:52:25 -04:00
} ,
// FlexVolume
{
name : "valid FlexVolume" ,
vol : api . Volume {
Name : "flex-volume" ,
VolumeSource : api . VolumeSource {
FlexVolume : & api . FlexVolumeSource {
Driver : "kubernetes.io/blue" ,
FSType : "ext4" ,
} ,
} ,
} ,
} ,
// AzureFile
{
name : "valid AzureFile" ,
vol : api . Volume {
Name : "azure-file" ,
VolumeSource : api . VolumeSource {
AzureFile : & api . AzureFileVolumeSource {
SecretName : "key" ,
ShareName : "share" ,
ReadOnly : false ,
} ,
} ,
} ,
} ,
{
name : "AzureFile empty secret" ,
vol : api . Volume {
Name : "azure-file" ,
VolumeSource : api . VolumeSource {
AzureFile : & api . AzureFileVolumeSource {
SecretName : "" ,
ShareName : "share" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "azureFile.secretName" ,
} ,
{
name : "AzureFile empty share" ,
vol : api . Volume {
Name : "azure-file" ,
VolumeSource : api . VolumeSource {
AzureFile : & api . AzureFileVolumeSource {
SecretName : "name" ,
ShareName : "" ,
ReadOnly : false ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "azureFile.shareName" ,
2016-05-23 18:08:22 -04:00
} ,
2016-04-20 04:38:19 -04:00
// Quobyte
{
name : "valid Quobyte" ,
vol : api . Volume {
Name : "quobyte" ,
VolumeSource : api . VolumeSource {
Quobyte : & api . QuobyteVolumeSource {
Registry : "registry:7861" ,
Volume : "volume" ,
ReadOnly : false ,
User : "root" ,
Group : "root" ,
} ,
} ,
} ,
} ,
{
name : "empty registry quobyte" ,
vol : api . Volume {
Name : "quobyte" ,
VolumeSource : api . VolumeSource {
Quobyte : & api . QuobyteVolumeSource {
Volume : "/test" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "quobyte.registry" ,
} ,
{
name : "wrong format registry quobyte" ,
vol : api . Volume {
Name : "quobyte" ,
VolumeSource : api . VolumeSource {
Quobyte : & api . QuobyteVolumeSource {
Registry : "registry7861" ,
Volume : "/test" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "quobyte.registry" ,
} ,
{
name : "wrong format multiple registries quobyte" ,
vol : api . Volume {
Name : "quobyte" ,
VolumeSource : api . VolumeSource {
Quobyte : & api . QuobyteVolumeSource {
Registry : "registry:7861,reg2" ,
Volume : "/test" ,
} ,
} ,
} ,
errtype : field . ErrorTypeInvalid ,
errfield : "quobyte.registry" ,
} ,
{
name : "empty volume quobyte" ,
vol : api . Volume {
Name : "quobyte" ,
VolumeSource : api . VolumeSource {
Quobyte : & api . QuobyteVolumeSource {
Registry : "registry:7861" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "quobyte.volume" ,
} ,
2016-07-27 14:07:34 -04:00
// AzureDisk
{
name : "valid AzureDisk" ,
vol : api . Volume {
Name : "azure-disk" ,
VolumeSource : api . VolumeSource {
AzureDisk : & api . AzureDiskVolumeSource {
DiskName : "foo" ,
DataDiskURI : "https://blob/vhds/bar.vhd" ,
} ,
} ,
} ,
} ,
{
name : "AzureDisk empty disk name" ,
vol : api . Volume {
Name : "azure-disk" ,
VolumeSource : api . VolumeSource {
AzureDisk : & api . AzureDiskVolumeSource {
DiskName : "" ,
DataDiskURI : "https://blob/vhds/bar.vhd" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "azureDisk.diskName" ,
} ,
{
name : "AzureDisk empty disk uri" ,
vol : api . Volume {
Name : "azure-disk" ,
VolumeSource : api . VolumeSource {
AzureDisk : & api . AzureDiskVolumeSource {
DiskName : "foo" ,
DataDiskURI : "" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "azureDisk.diskURI" ,
} ,
2016-11-19 15:46:23 -05:00
// ScaleIO
{
name : "valid scaleio volume" ,
vol : api . Volume {
Name : "scaleio-volume" ,
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
Gateway : "http://abcd/efg" ,
System : "test-system" ,
VolumeName : "test-vol-1" ,
} ,
} ,
} ,
} ,
{
name : "ScaleIO with empty name" ,
vol : api . Volume {
Name : "scaleio-volume" ,
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
Gateway : "http://abcd/efg" ,
System : "test-system" ,
VolumeName : "" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "scaleIO.volumeName" ,
} ,
{
name : "ScaleIO with empty gateway" ,
vol : api . Volume {
Name : "scaleio-volume" ,
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
Gateway : "" ,
System : "test-system" ,
VolumeName : "test-vol-1" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "scaleIO.gateway" ,
} ,
{
name : "ScaleIO with empty system" ,
vol : api . Volume {
Name : "scaleio-volume" ,
VolumeSource : api . VolumeSource {
ScaleIO : & api . ScaleIOVolumeSource {
Gateway : "http://agc/efg/gateway" ,
System : "" ,
VolumeName : "test-vol-1" ,
} ,
} ,
} ,
errtype : field . ErrorTypeRequired ,
errfield : "scaleIO.system" ,
} ,
2014-07-01 17:40:36 -04:00
}
2016-07-30 02:52:25 -04:00
for i , tc := range testCases {
2017-02-24 14:08:15 -05:00
names , errs := ValidateVolumes ( [ ] api . Volume { tc . vol } , field . NewPath ( "field" ) )
2016-07-30 02:52:25 -04:00
if len ( errs ) > 0 && tc . errtype == "" {
t . Errorf ( "[%d: %q] unexpected error(s): %v" , i , tc . name , errs )
} else if len ( errs ) > 1 {
t . Errorf ( "[%d: %q] expected 1 error, got %d: %v" , i , tc . name , len ( errs ) , errs )
} else if len ( errs ) == 0 && tc . errtype != "" {
t . Errorf ( "[%d: %q] expected error type %v" , i , tc . name , tc . errtype )
} else if len ( errs ) == 1 {
if errs [ 0 ] . Type != tc . errtype {
t . Errorf ( "[%d: %q] expected error type %v, got %v" , i , tc . name , tc . errtype , errs [ 0 ] . Type )
} else if ! strings . HasSuffix ( errs [ 0 ] . Field , "." + tc . errfield ) {
t . Errorf ( "[%d: %q] expected error on field %q, got %q" , i , tc . name , tc . errfield , errs [ 0 ] . Field )
} else if ! strings . Contains ( errs [ 0 ] . Detail , tc . errdetail ) {
t . Errorf ( "[%d: %q] expected error detail %q, got %q" , i , tc . name , tc . errdetail , errs [ 0 ] . Detail )
2014-08-19 23:54:20 -04:00
}
2016-07-30 02:52:25 -04:00
} else {
if len ( names ) != 1 || ! names . Has ( tc . vol . Name ) {
t . Errorf ( "[%d: %q] wrong names result: %v" , i , tc . name , names )
2015-02-04 19:36:27 -05:00
}
2014-07-01 18:56:30 -04:00
}
}
2016-07-30 02:52:25 -04:00
dupsCase := [ ] api . Volume {
{ Name : "abc" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
{ Name : "abc" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
}
2017-02-24 14:08:15 -05:00
_ , errs := ValidateVolumes ( dupsCase , field . NewPath ( "field" ) )
2016-07-30 02:52:25 -04:00
if len ( errs ) == 0 {
t . Errorf ( "expected error" )
} else if len ( errs ) != 1 {
t . Errorf ( "expected 1 error, got %d: %v" , len ( errs ) , errs )
} else if errs [ 0 ] . Type != field . ErrorTypeDuplicate {
t . Errorf ( "expected error type %v, got %v" , field . ErrorTypeDuplicate , errs [ 0 ] . Type )
}
2014-07-01 18:56:30 -04:00
}
2017-05-25 16:53:40 -04:00
func TestAlphaLocalStorageCapacityIsolation ( t * testing . T ) {
testCases := [ ] api . VolumeSource {
2017-08-04 14:50:38 -04:00
{ EmptyDir : & api . EmptyDirVolumeSource { SizeLimit : resource . NewQuantity ( int64 ( 5 ) , resource . BinarySI ) } } ,
2017-05-25 16:53:40 -04:00
}
// Enable alpha feature LocalStorageCapacityIsolation
err := utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , tc := range testCases {
2017-07-11 23:37:48 -04:00
if errs := validateVolumeSource ( & tc , field . NewPath ( "spec" ) , "tmpvol" ) ; len ( errs ) != 0 {
2017-05-25 16:53:40 -04:00
t . Errorf ( "expected success: %v" , errs )
}
}
// Disable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , tc := range testCases {
2017-07-11 23:37:48 -04:00
if errs := validateVolumeSource ( & tc , field . NewPath ( "spec" ) , "tmpvol" ) ; len ( errs ) == 0 {
2017-05-25 16:53:40 -04:00
t . Errorf ( "expected failure: %v" , errs )
}
}
containerLimitCase := api . ResourceRequirements {
Limits : api . ResourceList {
2017-08-24 00:43:06 -04:00
api . ResourceEphemeralStorage : * resource . NewMilliQuantity (
2017-05-25 16:53:40 -04:00
int64 ( 40000 ) ,
resource . BinarySI ) ,
} ,
}
// Enable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
if errs := ValidateResourceRequirements ( & containerLimitCase , field . NewPath ( "resources" ) ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
// Disable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
if errs := ValidateResourceRequirements ( & containerLimitCase , field . NewPath ( "resources" ) ) ; len ( errs ) == 0 {
t . Errorf ( "expected failure: %v" , errs )
}
}
2017-08-15 22:29:29 -04:00
func TestValidateResourceQuotaWithAlphaLocalStorageCapacityIsolation ( t * testing . T ) {
spec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
api . ResourceMemory : resource . MustParse ( "10000" ) ,
api . ResourceRequestsCPU : resource . MustParse ( "100" ) ,
api . ResourceRequestsMemory : resource . MustParse ( "10000" ) ,
api . ResourceLimitsCPU : resource . MustParse ( "100" ) ,
api . ResourceLimitsMemory : resource . MustParse ( "10000" ) ,
api . ResourcePods : resource . MustParse ( "10" ) ,
api . ResourceServices : resource . MustParse ( "0" ) ,
api . ResourceReplicationControllers : resource . MustParse ( "10" ) ,
api . ResourceQuotas : resource . MustParse ( "10" ) ,
api . ResourceConfigMaps : resource . MustParse ( "10" ) ,
api . ResourceSecrets : resource . MustParse ( "10" ) ,
api . ResourceEphemeralStorage : resource . MustParse ( "10000" ) ,
api . ResourceRequestsEphemeralStorage : resource . MustParse ( "10000" ) ,
api . ResourceLimitsEphemeralStorage : resource . MustParse ( "10000" ) ,
} ,
}
resourceQuota := & api . ResourceQuota {
ObjectMeta : metav1 . ObjectMeta {
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : spec ,
}
// Enable alpha feature LocalStorageCapacityIsolation
err := utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
if errs := ValidateResourceQuota ( resourceQuota ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
// Disable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
errs := ValidateResourceQuota ( resourceQuota )
if len ( errs ) == 0 {
t . Errorf ( "expected failure for %s" , resourceQuota . Name )
}
expectedErrMes := "ResourceEphemeralStorage field disabled by feature-gate for ResourceQuota"
for i := range errs {
if ! strings . Contains ( errs [ i ] . Detail , expectedErrMes ) {
t . Errorf ( "[%s]: expected error detail either empty or %s, got %s" , resourceQuota . Name , expectedErrMes , errs [ i ] . Detail )
}
}
}
2014-07-08 00:32:56 -04:00
func TestValidatePorts ( t * testing . T ) {
2015-02-23 17:25:56 -05:00
successCase := [ ] api . ContainerPort {
2014-07-08 00:32:56 -04:00
{ Name : "abc" , ContainerPort : 80 , HostPort : 80 , Protocol : "TCP" } ,
{ Name : "easy" , ContainerPort : 82 , Protocol : "TCP" } ,
{ Name : "as" , ContainerPort : 83 , Protocol : "UDP" } ,
2015-01-26 12:52:50 -05:00
{ Name : "do-re-me" , ContainerPort : 84 , Protocol : "UDP" } ,
{ ContainerPort : 85 , Protocol : "TCP" } ,
2014-07-08 00:32:56 -04:00
}
2015-11-06 18:30:52 -05:00
if errs := validateContainerPorts ( successCase , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2014-07-08 02:20:30 -04:00
t . Errorf ( "expected success: %v" , errs )
2014-07-08 00:32:56 -04:00
}
2015-02-23 17:25:56 -05:00
nonCanonicalCase := [ ] api . ContainerPort {
2015-01-26 12:52:50 -05:00
{ ContainerPort : 80 , Protocol : "TCP" } ,
2014-07-08 00:32:56 -04:00
}
2015-11-06 18:30:52 -05:00
if errs := validateContainerPorts ( nonCanonicalCase , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2014-07-08 02:20:30 -04:00
t . Errorf ( "expected success: %v" , errs )
2014-07-08 00:32:56 -04:00
}
2014-08-19 23:54:20 -04:00
errorCases := map [ string ] struct {
2015-02-23 17:25:56 -05:00
P [ ] api . ContainerPort
2015-11-06 18:30:52 -05:00
T field . ErrorType
2014-08-19 23:54:20 -04:00
F string
2015-02-04 19:36:27 -05:00
D string
2014-08-19 23:54:20 -04:00
} {
2015-11-04 16:52:14 -05:00
"name > 15 characters" : {
[ ] api . ContainerPort { { Name : strings . Repeat ( "a" , 16 ) , ContainerPort : 80 , Protocol : "TCP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeInvalid ,
2016-01-04 11:33:26 -05:00
"name" , "15" ,
2015-11-04 16:52:14 -05:00
} ,
2016-01-04 11:33:26 -05:00
"name contains invalid characters" : {
2015-11-04 16:52:14 -05:00
[ ] api . ContainerPort { { Name : "a.b.c" , ContainerPort : 80 , Protocol : "TCP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeInvalid ,
2016-01-04 11:33:26 -05:00
"name" , "alpha-numeric" ,
2015-11-04 16:52:14 -05:00
} ,
2016-01-04 11:33:26 -05:00
"name is a number" : {
2015-11-04 16:52:14 -05:00
[ ] api . ContainerPort { { Name : "80" , ContainerPort : 80 , Protocol : "TCP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeInvalid ,
2016-01-04 11:33:26 -05:00
"name" , "at least one letter" ,
2015-11-04 16:52:14 -05:00
} ,
"name not unique" : {
[ ] api . ContainerPort {
{ Name : "abc" , ContainerPort : 80 , Protocol : "TCP" } ,
{ Name : "abc" , ContainerPort : 81 , Protocol : "TCP" } ,
} ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeDuplicate ,
2015-11-04 16:52:14 -05:00
"[1].name" , "" ,
} ,
"zero container port" : {
[ ] api . ContainerPort { { ContainerPort : 0 , Protocol : "TCP" } } ,
2016-01-04 11:33:26 -05:00
field . ErrorTypeRequired ,
"containerPort" , "" ,
2015-11-04 16:52:14 -05:00
} ,
"invalid container port" : {
[ ] api . ContainerPort { { ContainerPort : 65536 , Protocol : "TCP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeInvalid ,
2016-01-04 11:33:26 -05:00
"containerPort" , "between" ,
2015-11-04 16:52:14 -05:00
} ,
"invalid host port" : {
[ ] api . ContainerPort { { ContainerPort : 80 , HostPort : 65536 , Protocol : "TCP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeInvalid ,
2016-01-04 11:33:26 -05:00
"hostPort" , "between" ,
2015-11-04 16:52:14 -05:00
} ,
"invalid protocol case" : {
[ ] api . ContainerPort { { ContainerPort : 80 , Protocol : "tcp" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeNotSupported ,
2015-11-04 16:52:14 -05:00
"protocol" , "supported values: TCP, UDP" ,
} ,
"invalid protocol" : {
[ ] api . ContainerPort { { ContainerPort : 80 , Protocol : "ICMP" } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeNotSupported ,
2015-11-04 16:52:14 -05:00
"protocol" , "supported values: TCP, UDP" ,
} ,
"protocol required" : {
[ ] api . ContainerPort { { Name : "abc" , ContainerPort : 80 } } ,
2015-11-06 18:30:52 -05:00
field . ErrorTypeRequired ,
2015-11-04 16:52:14 -05:00
"protocol" , "" ,
} ,
2014-07-08 00:32:56 -04:00
}
for k , v := range errorCases {
2015-11-06 18:30:52 -05:00
errs := validateContainerPorts ( v . P , field . NewPath ( "field" ) )
2014-08-19 23:54:20 -04:00
if len ( errs ) == 0 {
2014-07-08 00:32:56 -04:00
t . Errorf ( "expected failure for %s" , k )
}
2014-08-19 23:54:20 -04:00
for i := range errs {
2015-11-03 19:08:20 -05:00
if errs [ i ] . Type != v . T {
2015-11-04 16:52:14 -05:00
t . Errorf ( "%s: expected error to have type %q: %q" , k , v . T , errs [ i ] . Type )
2014-08-19 23:54:20 -04:00
}
2015-11-04 16:52:14 -05:00
if ! strings . Contains ( errs [ i ] . Field , v . F ) {
t . Errorf ( "%s: expected error field %q: %q" , k , v . F , errs [ i ] . Field )
2014-08-19 23:54:20 -04:00
}
2015-11-04 16:52:14 -05:00
if ! strings . Contains ( errs [ i ] . Detail , v . D ) {
t . Errorf ( "%s: expected error detail %q, got %q" , k , v . D , errs [ i ] . Detail )
2015-02-04 19:36:27 -05:00
}
2014-08-19 23:54:20 -04:00
}
2014-07-08 00:32:56 -04:00
}
}
2017-08-10 03:26:07 -04:00
func TestLocalStorageEnvWithFeatureGate ( t * testing . T ) {
testCases := [ ] api . EnvVar {
{
Name : "ephemeral-storage-limits" ,
ValueFrom : & api . EnvVarSource {
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "limits.ephemeral-storage" ,
} ,
} ,
} ,
{
Name : "ephemeral-storage-requests" ,
ValueFrom : & api . EnvVarSource {
ResourceFieldRef : & api . ResourceFieldSelector {
ContainerName : "test-container" ,
Resource : "requests.ephemeral-storage" ,
} ,
} ,
} ,
}
// Enable alpha feature LocalStorageCapacityIsolation
err := utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , testCase := range testCases {
if errs := validateEnvVarValueFrom ( testCase , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
t . Errorf ( "expected success, got: %v" , errs )
}
}
// Disable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , testCase := range testCases {
if errs := validateEnvVarValueFrom ( testCase , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
t . Errorf ( "expected failure for %v" , testCase . Name )
}
}
}
2014-07-01 18:56:30 -04:00
func TestValidateEnv ( t * testing . T ) {
2014-08-29 21:20:27 -04:00
successCase := [ ] api . EnvVar {
2014-07-01 18:56:30 -04:00
{ Name : "abc" , Value : "value" } ,
{ Name : "ABC" , Value : "value" } ,
{ Name : "AbC_123" , Value : "value" } ,
{ Name : "abc" , Value : "" } ,
2017-06-17 18:34:22 -04:00
{ Name : "a.b.c" , Value : "value" } ,
{ Name : "a-b-c" , Value : "value" } ,
2015-04-23 16:57:30 -04:00
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2015-04-23 16:57:30 -04:00
FieldPath : "metadata.name" ,
} ,
} ,
} ,
2017-06-27 04:54:35 -04:00
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
FieldPath : "metadata.namespace" ,
} ,
} ,
} ,
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
FieldPath : "metadata.uid" ,
} ,
} ,
} ,
2016-06-22 12:27:17 -04:00
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2016-06-22 12:27:17 -04:00
FieldPath : "spec.nodeName" ,
} ,
} ,
} ,
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2016-06-22 12:27:17 -04:00
FieldPath : "spec.serviceAccountName" ,
} ,
} ,
} ,
2017-03-08 03:08:09 -05:00
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
FieldPath : "status.hostIP" ,
} ,
} ,
} ,
{
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
FieldPath : "status.podIP" ,
} ,
} ,
} ,
2016-01-18 15:07:48 -05:00
{
Name : "secret_value" ,
ValueFrom : & api . EnvVarSource {
SecretKeyRef : & api . SecretKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "some-secret" ,
} ,
Key : "secret-key" ,
} ,
} ,
} ,
2016-01-18 22:31:29 -05:00
{
Name : "ENV_VAR_1" ,
ValueFrom : & api . EnvVarSource {
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "some-config-map" ,
} ,
Key : "some-key" ,
} ,
} ,
} ,
2014-07-01 18:56:30 -04:00
}
2017-02-24 14:08:15 -05:00
if errs := ValidateEnv ( successCase , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2017-06-27 04:54:35 -04:00
t . Errorf ( "expected success, got: %v" , errs )
2014-07-01 18:56:30 -04:00
}
2015-04-23 16:57:30 -04:00
errorCases := [ ] struct {
name string
envs [ ] api . EnvVar
expectedError string
} {
{
name : "zero-length name" ,
envs : [ ] api . EnvVar { { Name : "" } } ,
2015-11-15 01:36:25 -05:00
expectedError : "[0].name: Required value" ,
2015-04-23 16:57:30 -04:00
} ,
{
2017-06-17 18:34:22 -04:00
name : "illegal character" ,
envs : [ ] api . EnvVar { { Name : "a!b" } } ,
expectedError : ` [0].name: Invalid value: "a!b": ` + envVarNameErrMsg ,
} ,
{
name : "dot only" ,
envs : [ ] api . EnvVar { { Name : "." } } ,
expectedError : ` [0].name: Invalid value: ".": must not be ` ,
} ,
{
name : "double dots only" ,
envs : [ ] api . EnvVar { { Name : ".." } } ,
expectedError : ` [0].name: Invalid value: "..": must not be ` ,
} ,
{
name : "leading double dots" ,
envs : [ ] api . EnvVar { { Name : "..abc" } } ,
expectedError : ` [0].name: Invalid value: "..abc": must not start with ` ,
2015-04-23 16:57:30 -04:00
} ,
{
name : "value and valueFrom specified" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
Value : "foo" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2015-04-23 16:57:30 -04:00
FieldPath : "metadata.name" ,
} ,
} ,
} } ,
2015-11-15 01:36:25 -05:00
expectedError : "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty" ,
2015-04-23 16:57:30 -04:00
} ,
2016-11-08 11:48:12 -05:00
{
name : "valueFrom without a source" ,
envs : [ ] api . EnvVar { {
2016-11-15 04:36:34 -05:00
Name : "abc" ,
ValueFrom : & api . EnvVarSource { } ,
2016-11-08 11:48:12 -05:00
} } ,
expectedError : "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`" ,
} ,
2016-01-18 15:07:48 -05:00
{
2016-01-18 22:31:29 -05:00
name : "valueFrom.fieldRef and valueFrom.secretKeyRef specified" ,
2016-01-18 15:07:48 -05:00
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2016-01-18 15:07:48 -05:00
FieldPath : "metadata.name" ,
} ,
SecretKeyRef : & api . SecretKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "a-secret" ,
} ,
Key : "a-key" ,
} ,
} ,
} } ,
expectedError : "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time" ,
} ,
2016-01-18 22:31:29 -05:00
{
name : "valueFrom.fieldRef and valueFrom.configMapKeyRef set" ,
envs : [ ] api . EnvVar { {
Name : "some_var_name" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2016-01-18 22:31:29 -05:00
FieldPath : "metadata.name" ,
} ,
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "some-config-map" ,
} ,
Key : "some-key" ,
} ,
} ,
} } ,
expectedError : ` [0].valueFrom: Invalid value: "": may not have more than one field specified at a time ` ,
} ,
{
name : "valueFrom.fieldRef and valueFrom.secretKeyRef specified" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2016-01-18 22:31:29 -05:00
FieldPath : "metadata.name" ,
} ,
SecretKeyRef : & api . SecretKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "a-secret" ,
} ,
Key : "a-key" ,
} ,
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "some-config-map" ,
} ,
Key : "some-key" ,
} ,
} ,
} } ,
expectedError : ` [0].valueFrom: Invalid value: "": may not have more than one field specified at a time ` ,
} ,
2017-06-03 11:43:46 -04:00
{
name : "valueFrom.secretKeyRef.name invalid" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
SecretKeyRef : & api . SecretKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "$%^&*#" ,
} ,
Key : "a-key" ,
} ,
} ,
} } ,
} ,
{
name : "valueFrom.configMapKeyRef.name invalid" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
ConfigMapKeyRef : & api . ConfigMapKeySelector {
LocalObjectReference : api . LocalObjectReference {
Name : "$%^&*#" ,
} ,
Key : "some-key" ,
} ,
} ,
} } ,
} ,
2015-04-23 16:57:30 -04:00
{
name : "missing FieldPath on ObjectFieldSelector" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2015-04-23 16:57:30 -04:00
} ,
} ,
} } ,
2015-11-15 01:36:25 -05:00
expectedError : ` [0].valueFrom.fieldRef.fieldPath: Required value ` ,
2015-04-23 16:57:30 -04:00
} ,
{
name : "missing APIVersion on ObjectFieldSelector" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2015-04-23 16:57:30 -04:00
FieldPath : "metadata.name" ,
} ,
} ,
} } ,
2015-11-15 01:36:25 -05:00
expectedError : ` [0].valueFrom.fieldRef.apiVersion: Required value ` ,
2015-04-23 16:57:30 -04:00
} ,
{
name : "invalid fieldPath" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2015-04-23 16:57:30 -04:00
FieldPath : "metadata.whoops" ,
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2015-04-23 16:57:30 -04:00
} ,
} ,
} } ,
2015-11-15 01:36:25 -05:00
expectedError : ` [0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath ` ,
2015-04-23 16:57:30 -04:00
} ,
2015-02-20 00:36:23 -05:00
{
name : "invalid fieldPath labels" ,
envs : [ ] api . EnvVar { {
Name : "labels" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
FieldPath : "metadata.labels" ,
APIVersion : "v1" ,
} ,
} ,
} } ,
2017-06-27 04:54:35 -04:00
expectedError : ` [0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: metadata.name, metadata.namespace, metadata.uid, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP ` ,
2015-02-20 00:36:23 -05:00
} ,
{
name : "invalid fieldPath annotations" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
FieldRef : & api . ObjectFieldSelector {
FieldPath : "metadata.annotations" ,
APIVersion : "v1" ,
} ,
} ,
} } ,
2017-06-27 04:54:35 -04:00
expectedError : ` [0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: metadata.name, metadata.namespace, metadata.uid, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP ` ,
2015-02-20 00:36:23 -05:00
} ,
2015-04-23 16:57:30 -04:00
{
name : "unsupported fieldPath" ,
envs : [ ] api . EnvVar { {
Name : "abc" ,
ValueFrom : & api . EnvVarSource {
2015-05-04 13:31:36 -04:00
FieldRef : & api . ObjectFieldSelector {
2015-04-23 16:57:30 -04:00
FieldPath : "status.phase" ,
2017-01-12 13:17:43 -05:00
APIVersion : api . Registry . GroupOrDie ( api . GroupName ) . GroupVersion . String ( ) ,
2015-04-23 16:57:30 -04:00
} ,
} ,
} } ,
2017-06-27 04:54:35 -04:00
expectedError : ` valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: metadata.name, metadata.namespace, metadata.uid, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP ` ,
2015-04-23 16:57:30 -04:00
} ,
2014-07-01 18:56:30 -04:00
}
2015-04-23 16:57:30 -04:00
for _ , tc := range errorCases {
2017-02-24 14:08:15 -05:00
if errs := ValidateEnv ( tc . envs , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2015-04-23 16:57:30 -04:00
t . Errorf ( "expected failure for %s" , tc . name )
2015-02-04 19:36:27 -05:00
} else {
for i := range errs {
2015-11-03 19:08:20 -05:00
str := errs [ i ] . Error ( )
2015-11-04 16:52:14 -05:00
if str != "" && ! strings . Contains ( str , tc . expectedError ) {
t . Errorf ( "%s: expected error detail either empty or %q, got %q" , tc . name , tc . expectedError , str )
2015-02-04 19:36:27 -05:00
}
}
2014-07-01 17:40:36 -04:00
}
}
}
2016-11-29 21:57:35 -05:00
func TestValidateEnvFrom ( t * testing . T ) {
successCase := [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
{
Prefix : "pre_" ,
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
2017-06-17 18:34:22 -04:00
{
Prefix : "a.b" ,
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
2017-01-04 15:50:11 -05:00
{
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
{
Prefix : "pre_" ,
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
2017-06-17 18:34:22 -04:00
{
Prefix : "a.b" ,
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } ,
} ,
} ,
2016-11-29 21:57:35 -05:00
}
2017-02-24 14:08:15 -05:00
if errs := ValidateEnvFrom ( successCase , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2016-11-29 21:57:35 -05:00
t . Errorf ( "expected success: %v" , errs )
}
errorCases := [ ] struct {
name string
envs [ ] api . EnvFromSource
expectedError string
} {
{
name : "zero-length name" ,
envs : [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "" } } ,
} ,
} ,
expectedError : "field[0].configMapRef.name: Required value" ,
} ,
2017-02-24 13:07:20 -05:00
{
name : "invalid name" ,
envs : [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "$" } } ,
} ,
} ,
expectedError : "field[0].configMapRef.name: Invalid value" ,
} ,
2016-11-29 21:57:35 -05:00
{
name : "invalid prefix" ,
envs : [ ] api . EnvFromSource {
{
2017-06-17 18:34:22 -04:00
Prefix : "a!b" ,
2016-11-29 21:57:35 -05:00
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } } ,
} ,
} ,
2017-06-17 18:34:22 -04:00
expectedError : ` field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg ,
2016-11-29 21:57:35 -05:00
} ,
2017-01-04 15:50:11 -05:00
{
name : "zero-length name" ,
envs : [ ] api . EnvFromSource {
{
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "" } } ,
} ,
} ,
expectedError : "field[0].secretRef.name: Required value" ,
} ,
2017-02-24 13:07:20 -05:00
{
name : "invalid name" ,
envs : [ ] api . EnvFromSource {
{
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "&" } } ,
} ,
} ,
expectedError : "field[0].secretRef.name: Invalid value" ,
} ,
2017-01-04 15:50:11 -05:00
{
name : "invalid prefix" ,
envs : [ ] api . EnvFromSource {
{
2017-06-17 18:34:22 -04:00
Prefix : "a!b" ,
2017-01-04 15:50:11 -05:00
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } } ,
} ,
} ,
2017-06-17 18:34:22 -04:00
expectedError : ` field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg ,
2017-01-04 15:50:11 -05:00
} ,
{
name : "no refs" ,
envs : [ ] api . EnvFromSource {
{ } ,
} ,
expectedError : "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`" ,
} ,
{
name : "multiple refs" ,
envs : [ ] api . EnvFromSource {
{
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } } ,
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "abc" } } ,
} ,
} ,
expectedError : "field: Invalid value: \"\": may not have more than one field specified at a time" ,
} ,
2017-06-03 11:43:46 -04:00
{
name : "invalid secret ref name" ,
envs : [ ] api . EnvFromSource {
{
SecretRef : & api . SecretEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "$%^&*#" } } ,
} ,
} ,
expectedError : "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg ,
} ,
{
name : "invalid config ref name" ,
envs : [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference { Name : "$%^&*#" } } ,
} ,
} ,
expectedError : "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg ,
} ,
2016-11-29 21:57:35 -05:00
}
for _ , tc := range errorCases {
2017-02-24 14:08:15 -05:00
if errs := ValidateEnvFrom ( tc . envs , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2016-11-29 21:57:35 -05:00
t . Errorf ( "expected failure for %s" , tc . name )
} else {
for i := range errs {
str := errs [ i ] . Error ( )
if str != "" && ! strings . Contains ( str , tc . expectedError ) {
t . Errorf ( "%s: expected error detail either empty or %q, got %q" , tc . name , tc . expectedError , str )
}
}
}
}
}
2014-07-04 22:46:56 -04:00
func TestValidateVolumeMounts ( t * testing . T ) {
2015-09-09 13:45:01 -04:00
volumes := sets . NewString ( "abc" , "123" , "abc-123" )
2017-09-01 06:05:55 -04:00
container := api . Container {
SecurityContext : nil ,
}
propagation := api . MountPropagationBidirectional
2014-07-04 22:46:56 -04:00
2014-08-29 21:20:27 -04:00
successCase := [ ] api . VolumeMount {
2014-07-04 22:46:56 -04:00
{ Name : "abc" , MountPath : "/foo" } ,
2016-03-17 23:52:34 -04:00
{ Name : "123" , MountPath : "/bar" } ,
{ Name : "abc-123" , MountPath : "/baz" } ,
2016-03-04 20:40:15 -05:00
{ Name : "abc-123" , MountPath : "/baa" , SubPath : "" } ,
{ Name : "abc-123" , MountPath : "/bab" , SubPath : "baz" } ,
{ Name : "abc-123" , MountPath : "/bac" , SubPath : ".baz" } ,
{ Name : "abc-123" , MountPath : "/bad" , SubPath : "..baz" } ,
2014-07-04 22:46:56 -04:00
}
2017-09-01 06:05:55 -04:00
if errs := ValidateVolumeMounts ( successCase , volumes , & container , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2014-07-08 02:20:30 -04:00
t . Errorf ( "expected success: %v" , errs )
2014-07-04 22:46:56 -04:00
}
2014-08-29 21:20:27 -04:00
errorCases := map [ string ] [ ] api . VolumeMount {
2017-09-01 06:05:55 -04:00
"empty name" : { { Name : "" , MountPath : "/foo" } } ,
"name not found" : { { Name : "" , MountPath : "/foo" } } ,
"empty mountpath" : { { Name : "abc" , MountPath : "" } } ,
"relative mountpath" : { { Name : "abc" , MountPath : "bar" } } ,
"mountpath collision" : { { Name : "foo" , MountPath : "/path/a" } , { Name : "bar" , MountPath : "/path/a" } } ,
"absolute subpath" : { { Name : "abc" , MountPath : "/bar" , SubPath : "/baz" } } ,
"subpath in .." : { { Name : "abc" , MountPath : "/bar" , SubPath : "../baz" } } ,
"subpath contains .." : { { Name : "abc" , MountPath : "/bar" , SubPath : "baz/../bat" } } ,
"subpath ends in .." : { { Name : "abc" , MountPath : "/bar" , SubPath : "./.." } } ,
"disabled MountPropagation feature gate" : { { Name : "abc" , MountPath : "/bar" , MountPropagation : & propagation } } ,
2014-07-04 22:46:56 -04:00
}
for k , v := range errorCases {
2017-09-01 06:05:55 -04:00
if errs := ValidateVolumeMounts ( v , volumes , & container , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2014-07-04 22:46:56 -04:00
t . Errorf ( "expected failure for %s" , k )
}
}
}
2017-09-01 06:05:55 -04:00
func TestValidateMountPropagation ( t * testing . T ) {
bTrue := true
bFalse := false
privilegedContainer := & api . Container {
SecurityContext : & api . SecurityContext {
Privileged : & bTrue ,
} ,
}
nonPrivilegedContainer := & api . Container {
SecurityContext : & api . SecurityContext {
Privileged : & bFalse ,
} ,
}
defaultContainer := & api . Container { }
propagationBidirectional := api . MountPropagationBidirectional
propagationHostToContainer := api . MountPropagationHostToContainer
propagationInvalid := api . MountPropagationMode ( "invalid" )
tests := [ ] struct {
mount api . VolumeMount
container * api . Container
expectError bool
} {
{
// implicitly non-privileged container + no propagation
api . VolumeMount { Name : "foo" , MountPath : "/foo" } ,
defaultContainer ,
false ,
} ,
{
// implicitly non-privileged container + HostToContainer
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationHostToContainer } ,
defaultContainer ,
false ,
} ,
{
// error: implicitly non-privileged container + Bidirectional
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationBidirectional } ,
defaultContainer ,
true ,
} ,
{
// explicitly non-privileged container + no propagation
api . VolumeMount { Name : "foo" , MountPath : "/foo" } ,
nonPrivilegedContainer ,
false ,
} ,
{
// explicitly non-privileged container + HostToContainer
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationHostToContainer } ,
nonPrivilegedContainer ,
false ,
} ,
{
// explicitly non-privileged container + HostToContainer
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationBidirectional } ,
nonPrivilegedContainer ,
true ,
} ,
{
// privileged container + no propagation
api . VolumeMount { Name : "foo" , MountPath : "/foo" } ,
privilegedContainer ,
false ,
} ,
{
// privileged container + HostToContainer
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationHostToContainer } ,
privilegedContainer ,
false ,
} ,
{
// privileged container + Bidirectional
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationBidirectional } ,
privilegedContainer ,
false ,
} ,
{
// error: privileged container + invalid mount propagation
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationInvalid } ,
privilegedContainer ,
true ,
} ,
{
// no container + Bidirectional
api . VolumeMount { Name : "foo" , MountPath : "/foo" , MountPropagation : & propagationBidirectional } ,
nil ,
false ,
} ,
}
// Enable MountPropagation for this test
priorityEnabled := utilfeature . DefaultFeatureGate . Enabled ( "MountPropagation" )
defer func ( ) {
var err error
// restoring the old value
if priorityEnabled {
err = utilfeature . DefaultFeatureGate . Set ( "MountPropagation=true" )
} else {
err = utilfeature . DefaultFeatureGate . Set ( "MountPropagation=false" )
}
if err != nil {
t . Errorf ( "Failed to restore feature gate for MountPropagation: %v" , err )
}
} ( )
err := utilfeature . DefaultFeatureGate . Set ( "MountPropagation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for MountPropagation: %v" , err )
return
}
for i , test := range tests {
volumes := sets . NewString ( "foo" )
errs := ValidateVolumeMounts ( [ ] api . VolumeMount { test . mount } , volumes , test . container , field . NewPath ( "field" ) )
if test . expectError && len ( errs ) == 0 {
t . Errorf ( "test %d expected error, got none" , i )
}
if ! test . expectError && len ( errs ) != 0 {
t . Errorf ( "test %d expected success, got error: %v" , i , errs )
}
}
}
2015-02-15 02:02:07 -05:00
func TestValidateProbe ( t * testing . T ) {
handler := api . Handler { Exec : & api . ExecAction { Command : [ ] string { "echo" } } }
2015-11-05 18:38:46 -05:00
// These fields must be positive.
positiveFields := [ ... ] string { "InitialDelaySeconds" , "TimeoutSeconds" , "PeriodSeconds" , "SuccessThreshold" , "FailureThreshold" }
successCases := [ ] * api . Probe { nil }
for _ , field := range positiveFields {
probe := & api . Probe { Handler : handler }
reflect . ValueOf ( probe ) . Elem ( ) . FieldByName ( field ) . SetInt ( 10 )
successCases = append ( successCases , probe )
2015-02-15 02:02:07 -05:00
}
2015-11-05 18:38:46 -05:00
2015-02-15 02:02:07 -05:00
for _ , p := range successCases {
2015-11-06 18:30:52 -05:00
if errs := validateProbe ( p , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2015-02-15 02:02:07 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
2015-11-05 18:38:46 -05:00
errorCases := [ ] * api . Probe { { TimeoutSeconds : 10 , InitialDelaySeconds : 10 } }
for _ , field := range positiveFields {
probe := & api . Probe { Handler : handler }
reflect . ValueOf ( probe ) . Elem ( ) . FieldByName ( field ) . SetInt ( - 10 )
errorCases = append ( errorCases , probe )
2015-02-15 02:02:07 -05:00
}
for _ , p := range errorCases {
2015-11-06 18:30:52 -05:00
if errs := validateProbe ( p , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2015-02-15 02:02:07 -05:00
t . Errorf ( "expected failure for %v" , p )
}
}
}
2015-02-27 20:33:58 -05:00
func TestValidateHandler ( t * testing . T ) {
successCases := [ ] api . Handler {
{ Exec : & api . ExecAction { Command : [ ] string { "echo" } } } ,
2015-11-10 01:28:45 -05:00
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromInt ( 1 ) , Host : "" , Scheme : "HTTP" } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "/foo" , Port : intstr . FromInt ( 65535 ) , Host : "host" , Scheme : "HTTP" } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromString ( "port" ) , Host : "" , Scheme : "HTTP" } } ,
2016-07-25 17:34:05 -04:00
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromString ( "port" ) , Host : "" , Scheme : "HTTP" , HTTPHeaders : [ ] api . HTTPHeader { { Name : "Host" , Value : "foo.example.com" } } } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromString ( "port" ) , Host : "" , Scheme : "HTTP" , HTTPHeaders : [ ] api . HTTPHeader { { Name : "X-Forwarded-For" , Value : "1.2.3.4" } , { Name : "X-Forwarded-For" , Value : "5.6.7.8" } } } } ,
2015-02-27 20:33:58 -05:00
}
for _ , h := range successCases {
2015-11-06 18:30:52 -05:00
if errs := validateHandler ( & h , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2015-02-27 20:33:58 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
errorCases := [ ] api . Handler {
{ } ,
{ Exec : & api . ExecAction { Command : [ ] string { } } } ,
2015-11-10 01:28:45 -05:00
{ HTTPGet : & api . HTTPGetAction { Path : "" , Port : intstr . FromInt ( 0 ) , Host : "" } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "/foo" , Port : intstr . FromInt ( 65536 ) , Host : "host" } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "" , Port : intstr . FromString ( "" ) , Host : "" } } ,
2016-07-25 17:34:05 -04:00
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromString ( "port" ) , Host : "" , Scheme : "HTTP" , HTTPHeaders : [ ] api . HTTPHeader { { Name : "Host:" , Value : "foo.example.com" } } } } ,
{ HTTPGet : & api . HTTPGetAction { Path : "/" , Port : intstr . FromString ( "port" ) , Host : "" , Scheme : "HTTP" , HTTPHeaders : [ ] api . HTTPHeader { { Name : "X_Forwarded_For" , Value : "foo.example.com" } } } } ,
2015-02-27 20:33:58 -05:00
}
for _ , h := range errorCases {
2015-11-06 18:30:52 -05:00
if errs := validateHandler ( & h , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2015-02-27 20:33:58 -05:00
t . Errorf ( "expected failure for %#v" , h )
}
}
}
2015-01-16 18:02:36 -05:00
func TestValidatePullPolicy ( t * testing . T ) {
type T struct {
Container api . Container
ExpectedPolicy api . PullPolicy
}
testCases := map [ string ] T {
"NotPresent1" : {
2016-12-21 23:54:12 -05:00
api . Container { Name : "abc" , Image : "image:latest" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2015-01-16 18:02:36 -05:00
api . PullIfNotPresent ,
} ,
"NotPresent2" : {
2016-12-21 23:54:12 -05:00
api . Container { Name : "abc1" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2015-01-16 18:02:36 -05:00
api . PullIfNotPresent ,
} ,
"Always1" : {
2015-01-20 23:30:42 -05:00
api . Container { Name : "123" , Image : "image:latest" , ImagePullPolicy : "Always" } ,
2015-01-16 18:02:36 -05:00
api . PullAlways ,
} ,
"Always2" : {
2015-01-20 23:30:42 -05:00
api . Container { Name : "1234" , Image : "image" , ImagePullPolicy : "Always" } ,
2015-01-16 18:02:36 -05:00
api . PullAlways ,
} ,
"Never1" : {
2015-01-20 23:30:42 -05:00
api . Container { Name : "abc-123" , Image : "image:latest" , ImagePullPolicy : "Never" } ,
2015-01-16 18:02:36 -05:00
api . PullNever ,
} ,
"Never2" : {
2015-01-20 23:30:42 -05:00
api . Container { Name : "abc-1234" , Image : "image" , ImagePullPolicy : "Never" } ,
2015-01-16 18:02:36 -05:00
api . PullNever ,
} ,
}
for k , v := range testCases {
ctr := & v . Container
2015-11-06 18:30:52 -05:00
errs := validatePullPolicy ( ctr . ImagePullPolicy , field . NewPath ( "field" ) )
2015-01-16 18:02:36 -05:00
if len ( errs ) != 0 {
t . Errorf ( "case[%s] expected success, got %#v" , k , errs )
}
if ctr . ImagePullPolicy != v . ExpectedPolicy {
t . Errorf ( "case[%s] expected policy %v, got %v" , k , v . ExpectedPolicy , ctr . ImagePullPolicy )
}
}
}
2015-01-24 23:19:36 -05:00
func getResourceLimits ( cpu , memory string ) api . ResourceList {
res := api . ResourceList { }
res [ api . ResourceCPU ] = resource . MustParse ( cpu )
res [ api . ResourceMemory ] = resource . MustParse ( memory )
return res
}
2014-07-01 18:14:25 -04:00
func TestValidateContainers ( t * testing . T ) {
2015-09-09 13:45:01 -04:00
volumes := sets . String { }
2014-09-16 18:18:33 -04:00
capabilities . SetForTests ( capabilities . Capabilities {
2014-09-16 10:04:12 -04:00
AllowPrivileged : true ,
} )
2014-07-01 18:14:25 -04:00
2014-08-29 21:20:27 -04:00
successCase := [ ] api . Container {
2016-12-21 23:54:12 -05:00
{ Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2017-06-14 19:52:31 -04:00
// backwards compatibility to ensure containers in pod template spec do not check for this
{ Name : "def" , Image : " " , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
{ Name : "ghi" , Image : " some " , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2016-12-21 23:54:12 -05:00
{ Name : "123" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
{ Name : "abc-123" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2014-09-12 19:04:10 -04:00
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler {
Exec : & api . ExecAction { Command : [ ] string { "ls" , "-l" } } ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2014-09-12 19:04:10 -04:00
} ,
2015-01-24 23:19:36 -05:00
{
Name : "resources-test" ,
Image : "image" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-07-14 02:49:46 -04:00
api . ResourceName ( "my.org/resource" ) : resource . MustParse ( "10" ) ,
2015-01-24 23:19:36 -05:00
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-01-24 23:19:36 -05:00
} ,
2016-04-26 20:54:19 -04:00
{
2016-07-10 16:09:16 -04:00
Name : "resources-test-with-gpu-with-request" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "1" ) ,
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "1" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2016-07-10 16:09:16 -04:00
} ,
{
Name : "resources-test-with-gpu-without-request" ,
2016-04-26 20:54:19 -04:00
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "1" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2016-04-26 20:54:19 -04:00
} ,
2015-07-30 15:59:22 -04:00
{
Name : "resources-request-limit-simple" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "8" ) ,
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
{
Name : "resources-request-limit-edge" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-07-14 02:49:46 -04:00
api . ResourceName ( "my.org/resource" ) : resource . MustParse ( "10" ) ,
2015-07-30 15:59:22 -04:00
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-07-14 02:49:46 -04:00
api . ResourceName ( "my.org/resource" ) : resource . MustParse ( "10" ) ,
2015-07-30 15:59:22 -04:00
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
{
Name : "resources-request-limit-partials" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "9.5" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
2017-07-14 02:49:46 -04:00
api . ResourceName ( "my.org/resource" ) : resource . MustParse ( "10" ) ,
2015-07-30 15:59:22 -04:00
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
{
Name : "resources-request" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "9.5" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
2015-05-19 01:18:40 -04:00
{
Name : "same-host-port-different-protocol" ,
Image : "image" ,
Ports : [ ] api . ContainerPort {
{ ContainerPort : 80 , HostPort : 80 , Protocol : "TCP" } ,
{ ContainerPort : 80 , HostPort : 80 , Protocol : "UDP" } ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-05-19 01:18:40 -04:00
} ,
2016-12-21 23:54:12 -05:00
{
Name : "fallback-to-logs-termination-message" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "FallbackToLogsOnError" ,
} ,
{
Name : "file-termination-message" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
} ,
2017-06-03 11:43:46 -04:00
{
Name : "env-from-source" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
EnvFrom : [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "test" ,
} ,
} ,
} ,
} ,
} ,
2016-12-21 23:54:12 -05:00
{ Name : "abc-1234" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" , SecurityContext : fakeValidSecurityContext ( true ) } ,
2014-07-01 18:14:25 -04:00
}
2015-11-06 18:30:52 -05:00
if errs := validateContainers ( successCase , volumes , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2014-07-08 02:20:30 -04:00
t . Errorf ( "expected success: %v" , errs )
2014-07-01 18:14:25 -04:00
}
2014-09-16 18:18:33 -04:00
capabilities . SetForTests ( capabilities . Capabilities {
2014-09-16 10:04:12 -04:00
AllowPrivileged : false ,
} )
2014-08-29 21:20:27 -04:00
errorCases := map [ string ] [ ] api . Container {
2016-12-21 23:54:12 -05:00
"zero-length name" : { { Name : "" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2017-06-14 19:52:31 -04:00
"zero-length-image" : { { Name : "abc" , Image : "" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-12-21 23:54:12 -05:00
"name > 63 characters" : { { Name : strings . Repeat ( "a" , 64 ) , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
"name not a DNS label" : { { Name : "a.b.c" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2014-07-01 18:14:25 -04:00
"name not unique" : {
2016-12-21 23:54:12 -05:00
{ Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
{ Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2014-07-01 18:14:25 -04:00
} ,
2016-12-21 23:54:12 -05:00
"zero-length image" : { { Name : "abc" , Image : "" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2014-07-08 00:32:56 -04:00
"host port not unique" : {
2015-02-23 17:25:56 -05:00
{ Name : "abc" , Image : "image" , Ports : [ ] api . ContainerPort { { ContainerPort : 80 , HostPort : 80 , Protocol : "TCP" } } ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2015-02-23 17:25:56 -05:00
{ Name : "def" , Image : "image" , Ports : [ ] api . ContainerPort { { ContainerPort : 81 , HostPort : 80 , Protocol : "TCP" } } ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2014-07-08 00:32:56 -04:00
} ,
2014-07-01 18:56:30 -04:00
"invalid env var name" : {
2017-06-17 18:34:22 -04:00
{ Name : "abc" , Image : "image" , Env : [ ] api . EnvVar { { Name : "ev!1" } } , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2014-07-01 18:56:30 -04:00
} ,
2014-07-04 22:46:56 -04:00
"unknown volume name" : {
2015-02-04 18:44:46 -05:00
{ Name : "abc" , Image : "image" , VolumeMounts : [ ] api . VolumeMount { { Name : "anything" , MountPath : "/foo" } } ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } ,
2014-07-04 22:46:56 -04:00
} ,
2014-09-12 19:04:10 -04:00
"invalid lifecycle, no exec command." : {
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler {
Exec : & api . ExecAction { } ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2014-09-12 19:04:10 -04:00
} ,
} ,
"invalid lifecycle, no http path." : {
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler {
HTTPGet : & api . HTTPGetAction { } ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2014-09-12 19:04:10 -04:00
} ,
} ,
2015-02-26 22:20:34 -05:00
"invalid lifecycle, no tcp socket port." : {
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler {
TCPSocket : & api . TCPSocketAction { } ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-02-26 22:20:34 -05:00
} ,
} ,
"invalid lifecycle, zero tcp socket port." : {
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler {
TCPSocket : & api . TCPSocketAction {
2015-11-10 01:28:45 -05:00
Port : intstr . FromInt ( 0 ) ,
2015-02-26 22:20:34 -05:00
} ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-02-26 22:20:34 -05:00
} ,
} ,
2014-09-12 19:04:10 -04:00
"invalid lifecycle, no action." : {
{
Name : "life-123" ,
Image : "image" ,
Lifecycle : & api . Lifecycle {
PreStop : & api . Handler { } ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2014-09-12 19:04:10 -04:00
} ,
} ,
2015-02-26 22:20:34 -05:00
"invalid liveness probe, no tcp socket port." : {
{
Name : "life-123" ,
Image : "image" ,
LivenessProbe : & api . Probe {
Handler : api . Handler {
TCPSocket : & api . TCPSocketAction { } ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-02-26 22:20:34 -05:00
} ,
} ,
"invalid liveness probe, no action." : {
{
Name : "life-123" ,
Image : "image" ,
LivenessProbe : & api . Probe {
Handler : api . Handler { } ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
} ,
} ,
"invalid message termination policy" : {
{
Name : "life-123" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "Unknown" ,
} ,
} ,
"empty message termination policy" : {
{
Name : "life-123" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "" ,
2015-02-26 22:20:34 -05:00
} ,
} ,
2014-09-16 10:04:12 -04:00
"privilege disabled" : {
2015-05-05 19:02:13 -04:00
{ Name : "abc" , Image : "image" , SecurityContext : fakeValidSecurityContext ( true ) } ,
2014-09-16 10:04:12 -04:00
} ,
2015-01-24 23:19:36 -05:00
"invalid compute resource" : {
{
Name : "abc-123" ,
Image : "image" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : api . ResourceList {
"disk" : resource . MustParse ( "10G" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-01-24 23:19:36 -05:00
} ,
} ,
"Resource CPU invalid" : {
{
Name : "abc-123" ,
Image : "image" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : getResourceLimits ( "-10" , "0" ) ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-01-24 23:19:36 -05:00
} ,
} ,
2015-04-20 14:56:15 -04:00
"Resource Requests CPU invalid" : {
{
Name : "abc-123" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : getResourceLimits ( "-10" , "0" ) ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-04-20 14:56:15 -04:00
} ,
} ,
2015-01-24 23:19:36 -05:00
"Resource Memory invalid" : {
{
Name : "abc-123" ,
Image : "image" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : getResourceLimits ( "0" , "-10" ) ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-01-24 23:19:36 -05:00
} ,
} ,
2016-07-10 16:09:16 -04:00
"Resource GPU limit must match request" : {
2016-04-26 20:54:19 -04:00
{
2016-07-10 16:09:16 -04:00
Name : "gpu-resource-request-limit" ,
2016-04-26 20:54:19 -04:00
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2016-07-10 16:09:16 -04:00
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "0" ) ,
2016-04-26 20:54:19 -04:00
} ,
Limits : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "1" ) ,
} ,
} ,
2016-12-21 23:54:12 -05:00
TerminationMessagePolicy : "File" ,
ImagePullPolicy : "IfNotPresent" ,
2016-04-26 20:54:19 -04:00
} ,
} ,
2017-08-06 22:13:11 -04:00
"Resource GPU invalid setting only request" : {
{
Name : "gpu-resource-request-limit" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( api . ResourceNvidiaGPU ) : resource . MustParse ( "1" ) ,
} ,
} ,
TerminationMessagePolicy : "File" ,
ImagePullPolicy : "IfNotPresent" ,
} ,
} ,
2015-07-30 15:59:22 -04:00
"Request limit simple invalid" : {
{
Name : "abc-123" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Limits : getResourceLimits ( "5" , "3" ) ,
Requests : getResourceLimits ( "6" , "3" ) ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
} ,
"Request limit multiple invalid" : {
{
Name : "abc-123" ,
Image : "image" ,
Resources : api . ResourceRequirements {
Limits : getResourceLimits ( "5" , "3" ) ,
Requests : getResourceLimits ( "6" , "4" ) ,
} ,
2016-12-21 23:54:12 -05:00
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
2015-07-30 15:59:22 -04:00
} ,
} ,
2017-06-03 11:43:46 -04:00
"Invalid env from" : {
{
Name : "env-from-source" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
TerminationMessagePolicy : "File" ,
EnvFrom : [ ] api . EnvFromSource {
{
ConfigMapRef : & api . ConfigMapEnvSource {
LocalObjectReference : api . LocalObjectReference {
Name : "$%^&*#" ,
} ,
} ,
} ,
} ,
} ,
} ,
2014-07-01 18:14:25 -04:00
}
for k , v := range errorCases {
2015-11-06 18:30:52 -05:00
if errs := validateContainers ( v , volumes , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2014-07-01 18:14:25 -04:00
t . Errorf ( "expected failure for %s" , k )
}
}
}
2014-08-26 14:25:17 -04:00
func TestValidateRestartPolicy ( t * testing . T ) {
successCases := [ ] api . RestartPolicy {
2015-03-13 21:38:07 -04:00
api . RestartPolicyAlways ,
api . RestartPolicyOnFailure ,
api . RestartPolicyNever ,
2014-08-26 14:25:17 -04:00
}
for _ , policy := range successCases {
2015-11-06 18:30:52 -05:00
if errs := validateRestartPolicy ( & policy , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2014-08-26 14:25:17 -04:00
t . Errorf ( "expected success: %v" , errs )
}
}
2015-03-13 21:38:07 -04:00
errorCases := [ ] api . RestartPolicy { "" , "newpolicy" }
2014-08-26 14:25:17 -04:00
for k , policy := range errorCases {
2015-11-06 18:30:52 -05:00
if errs := validateRestartPolicy ( & policy , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2014-10-09 20:06:32 -04:00
t . Errorf ( "expected failure for %d" , k )
2014-08-26 14:25:17 -04:00
}
}
2015-01-06 14:11:52 -05:00
}
2014-08-26 14:25:17 -04:00
2015-01-06 14:11:52 -05:00
func TestValidateDNSPolicy ( t * testing . T ) {
2015-01-26 12:52:50 -05:00
successCases := [ ] api . DNSPolicy { api . DNSClusterFirst , api . DNSDefault , api . DNSPolicy ( api . DNSClusterFirst ) }
2015-01-06 14:11:52 -05:00
for _ , policy := range successCases {
2015-11-06 18:30:52 -05:00
if errs := validateDNSPolicy ( & policy , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2015-01-06 14:11:52 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
errorCases := [ ] api . DNSPolicy { api . DNSPolicy ( "invalid" ) }
for _ , policy := range errorCases {
2015-11-06 18:30:52 -05:00
if errs := validateDNSPolicy ( & policy , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2015-01-06 14:11:52 -05:00
t . Errorf ( "expected failure for %v" , policy )
}
}
2014-08-26 14:25:17 -04:00
}
2015-01-06 14:11:52 -05:00
func TestValidatePodSpec ( t * testing . T ) {
2015-05-09 01:01:43 -04:00
activeDeadlineSeconds := int64 ( 30 )
2017-05-31 16:10:22 -04:00
activeDeadlineSecondsMax := int64 ( math . MaxInt32 )
2017-04-20 06:57:07 -04:00
2017-06-21 03:13:36 -04:00
minUserID := int64 ( 0 )
maxUserID := int64 ( 2147483647 )
minGroupID := int64 ( 0 )
maxGroupID := int64 ( 2147483647 )
2017-04-20 06:57:07 -04:00
2017-05-09 21:25:34 -04:00
priorityEnabled := utilfeature . DefaultFeatureGate . Enabled ( "PodPriority" )
defer func ( ) {
var err error
// restoring the old value
if priorityEnabled {
err = utilfeature . DefaultFeatureGate . Set ( "PodPriority=true" )
} else {
err = utilfeature . DefaultFeatureGate . Set ( "PodPriority=false" )
}
if err != nil {
t . Errorf ( "Failed to restore feature gate for PodPriority: %v" , err )
}
} ( )
err := utilfeature . DefaultFeatureGate . Set ( "PodPriority=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for PodPriority: %v" , err )
return
}
2015-01-06 14:11:52 -05:00
successCases := [ ] api . PodSpec {
{ // Populate basic fields, leave defaults for most.
2015-03-03 17:48:55 -05:00
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-26 12:52:50 -05:00
DNSPolicy : api . DNSClusterFirst ,
2015-01-06 14:11:52 -05:00
} ,
{ // Populate all fields.
Volumes : [ ] api . Volume {
2015-03-03 17:48:55 -05:00
{ Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
2015-01-06 14:11:52 -05:00
} ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
InitContainers : [ ] api . Container { { Name : "ictr" , Image : "iimage" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-07-04 16:30:54 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-06 14:11:52 -05:00
NodeSelector : map [ string ] string {
"key" : "value" ,
2014-10-23 16:51:34 -04:00
} ,
2015-05-22 19:40:57 -04:00
NodeName : "foobar" ,
2015-05-09 01:01:43 -04:00
DNSPolicy : api . DNSClusterFirst ,
ActiveDeadlineSeconds : & activeDeadlineSeconds ,
2015-06-18 22:35:42 -04:00
ServiceAccountName : "acct" ,
2015-01-06 14:11:52 -05:00
} ,
2017-05-30 14:57:06 -04:00
{ // Populate all fields with larger active deadline.
Volumes : [ ] api . Volume {
{ Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
} ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
InitContainers : [ ] api . Container { { Name : "ictr" , Image : "iimage" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
NodeSelector : map [ string ] string {
"key" : "value" ,
} ,
NodeName : "foobar" ,
DNSPolicy : api . DNSClusterFirst ,
ActiveDeadlineSeconds : & activeDeadlineSecondsMax ,
ServiceAccountName : "acct" ,
} ,
2015-03-23 19:34:35 -04:00
{ // Populate HostNetwork.
Containers : [ ] api . Container {
2016-12-21 23:54:12 -05:00
{ Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" ,
Ports : [ ] api . ContainerPort {
{ HostPort : 8080 , ContainerPort : 8080 , Protocol : "TCP" } } ,
2015-03-23 19:34:35 -04:00
} ,
} ,
2015-09-14 17:56:51 -04:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : true ,
} ,
2015-03-23 19:34:35 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2015-11-19 21:42:02 -05:00
{ // Populate RunAsUser SupplementalGroups FSGroup with minID 0
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
2017-06-21 03:13:36 -04:00
SupplementalGroups : [ ] int64 { minGroupID } ,
2017-04-20 06:57:07 -04:00
RunAsUser : & minUserID ,
FSGroup : & minGroupID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
{ // Populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
2017-06-21 03:13:36 -04:00
SupplementalGroups : [ ] int64 { maxGroupID } ,
2017-04-20 06:57:07 -04:00
RunAsUser : & maxUserID ,
FSGroup : & maxGroupID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2015-09-21 11:34:02 -04:00
{ // Populate HostIPC.
2015-09-14 17:56:51 -04:00
SecurityContext : & api . PodSecurityContext {
HostIPC : true ,
} ,
2015-09-21 11:34:02 -04:00
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-09-21 11:34:02 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
{ // Populate HostPID.
2015-09-14 17:56:51 -04:00
SecurityContext : & api . PodSecurityContext {
HostPID : true ,
} ,
2015-09-21 11:34:02 -04:00
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-09-21 11:34:02 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2016-01-26 18:03:18 -05:00
{ // Populate Affinity.
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-01-26 18:03:18 -05:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-04-26 16:22:09 -04:00
{ // Populate HostAliases.
HostAliases : [ ] api . HostAlias { { IP : "12.34.56.78" , Hostnames : [ ] string { "host1" , "host2" } } } ,
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-08-14 18:39:14 -04:00
{ // Populate HostAliases with `foo.bar` hostnames.
2017-06-01 19:45:42 -04:00
HostAliases : [ ] api . HostAlias { { IP : "12.34.56.78" , Hostnames : [ ] string { "host1.foo" , "host2.bar" } } } ,
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-08-14 18:39:14 -04:00
{ // Populate HostAliases with HostNetwork.
HostAliases : [ ] api . HostAlias { { IP : "12.34.56.78" , Hostnames : [ ] string { "host1.foo" , "host2.bar" } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
SecurityContext : & api . PodSecurityContext {
HostNetwork : true ,
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-05-09 21:25:34 -04:00
{ // Populate PriorityClassName.
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
PriorityClassName : "valid-name" ,
} ,
2015-01-06 14:11:52 -05:00
}
for i := range successCases {
2015-11-06 18:30:52 -05:00
if errs := ValidatePodSpec ( & successCases [ i ] , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2015-01-06 14:11:52 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
2015-05-09 01:01:43 -04:00
activeDeadlineSeconds = int64 ( 0 )
2017-05-31 16:10:22 -04:00
activeDeadlineSecondsTooLarge := int64 ( math . MaxInt32 + 1 )
2017-04-20 06:57:07 -04:00
2017-06-21 03:13:36 -04:00
minUserID = int64 ( - 1 )
maxUserID = int64 ( 2147483648 )
minGroupID = int64 ( - 1 )
maxGroupID = int64 ( 2147483648 )
2017-04-20 06:57:07 -04:00
2015-01-06 14:11:52 -05:00
failureCases := map [ string ] api . PodSpec {
"bad volume" : {
2015-02-04 18:44:46 -05:00
Volumes : [ ] api . Volume { { } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 18:44:46 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-03-18 11:00:18 -04:00
} ,
"no containers" : {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2014-07-22 14:45:12 -04:00
} ,
2015-01-06 14:11:52 -05:00
"bad container" : {
2015-02-04 18:44:46 -05:00
Containers : [ ] api . Container { { } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 18:44:46 -05:00
DNSPolicy : api . DNSClusterFirst ,
2015-01-06 14:11:52 -05:00
} ,
2016-07-04 16:30:54 -04:00
"bad init container" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-07-04 16:30:54 -04:00
InitContainers : [ ] api . Container { { } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2015-01-06 14:11:52 -05:00
"bad DNS policy" : {
2015-02-04 18:44:46 -05:00
DNSPolicy : api . DNSPolicy ( "invalid" ) ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-02-04 18:44:46 -05:00
} ,
2015-06-11 17:16:58 -04:00
"bad service account name" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-06-18 22:35:42 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
ServiceAccountName : "invalidName" ,
2015-06-11 17:16:58 -04:00
} ,
2015-02-04 18:44:46 -05:00
"bad restart policy" : {
2015-03-13 21:38:07 -04:00
RestartPolicy : "UnknowPolicy" ,
2015-02-04 18:44:46 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-01-06 14:11:52 -05:00
} ,
2015-03-23 19:34:35 -04:00
"with hostNetwork hostPort not equal to containerPort" : {
Containers : [ ] api . Container {
{ Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , Ports : [ ] api . ContainerPort {
{ HostPort : 8080 , ContainerPort : 2600 , Protocol : "TCP" } } ,
} ,
} ,
2015-09-14 17:56:51 -04:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : true ,
} ,
2015-03-23 19:34:35 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-04-26 16:22:09 -04:00
"with hostAliases with invalid IP" : {
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
} ,
HostAliases : [ ] api . HostAlias { { IP : "999.999.999.999" , Hostnames : [ ] string { "host1" , "host2" } } } ,
} ,
"with hostAliases with invalid hostname" : {
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
} ,
HostAliases : [ ] api . HostAlias { { IP : "12.34.56.78" , Hostnames : [ ] string { "@#$^#@#$" } } } ,
} ,
2015-11-19 21:42:02 -05:00
"bad supplementalGroups large than math.MaxInt32" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-06-21 03:13:36 -04:00
SupplementalGroups : [ ] int64 { maxGroupID , 1234 } ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
"bad supplementalGroups less than 0" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-06-21 03:13:36 -04:00
SupplementalGroups : [ ] int64 { minGroupID , 1234 } ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
"bad runAsUser large than math.MaxInt32" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-04-20 06:57:07 -04:00
RunAsUser : & maxUserID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
"bad runAsUser less than 0" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-04-20 06:57:07 -04:00
RunAsUser : & minUserID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
"bad fsGroup large than math.MaxInt32" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-04-20 06:57:07 -04:00
FSGroup : & maxGroupID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
"bad fsGroup less than 0" : {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-19 21:42:02 -05:00
SecurityContext : & api . PodSecurityContext {
HostNetwork : false ,
2017-04-20 06:57:07 -04:00
FSGroup : & minGroupID ,
2015-11-19 21:42:02 -05:00
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2015-05-09 01:01:43 -04:00
"bad-active-deadline-seconds" : {
Volumes : [ ] api . Volume {
{ Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
} ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-05-09 01:01:43 -04:00
RestartPolicy : api . RestartPolicyAlways ,
NodeSelector : map [ string ] string {
"key" : "value" ,
} ,
2015-05-22 19:40:57 -04:00
NodeName : "foobar" ,
2015-05-09 01:01:43 -04:00
DNSPolicy : api . DNSClusterFirst ,
ActiveDeadlineSeconds : & activeDeadlineSeconds ,
} ,
2017-05-30 14:57:06 -04:00
"active-deadline-seconds-too-large" : {
Volumes : [ ] api . Volume {
{ Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
} ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
NodeSelector : map [ string ] string {
"key" : "value" ,
} ,
NodeName : "foobar" ,
DNSPolicy : api . DNSClusterFirst ,
ActiveDeadlineSeconds : & activeDeadlineSecondsTooLarge ,
} ,
2015-11-30 14:35:34 -05:00
"bad nodeName" : {
NodeName : "node name" ,
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-11-30 14:35:34 -05:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-05-09 21:25:34 -04:00
"bad PriorityClassName" : {
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
PriorityClassName : "InvalidName" ,
} ,
2015-01-06 14:11:52 -05:00
}
for k , v := range failureCases {
2015-11-06 18:30:52 -05:00
if errs := ValidatePodSpec ( & v , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
2015-01-06 14:11:52 -05:00
t . Errorf ( "expected failure for %q" , k )
}
}
2017-05-09 21:25:34 -04:00
err = utilfeature . DefaultFeatureGate . Set ( "PodPriority=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for PodPriority: %v" , err )
return
}
priority := int32 ( 100 )
featuregatedCases := map [ string ] api . PodSpec {
"set PriorityClassName" : {
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
PriorityClassName : "valid-name" ,
} ,
"set Priority" : {
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Priority : & priority ,
} ,
}
for k , v := range featuregatedCases {
if errs := ValidatePodSpec ( & v , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
t . Errorf ( "expected failure due to gated feature: %q" , k )
}
}
2015-01-06 14:11:52 -05:00
}
2017-02-07 09:08:45 -05:00
func extendPodSpecwithTolerations ( in api . PodSpec , tolerations [ ] api . Toleration ) api . PodSpec {
var out api . PodSpec
out . Containers = in . Containers
out . RestartPolicy = in . RestartPolicy
out . DNSPolicy = in . DNSPolicy
out . Tolerations = tolerations
return out
}
2015-01-06 14:11:52 -05:00
func TestValidatePod ( t * testing . T ) {
2016-12-08 10:14:21 -05:00
validPodSpec := func ( affinity * api . Affinity ) api . PodSpec {
spec := api . PodSpec {
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-12-08 10:14:21 -05:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
}
if affinity != nil {
spec . Affinity = affinity
}
return spec
2016-08-16 19:41:05 -04:00
}
2016-12-08 10:14:21 -05:00
2015-01-06 14:11:52 -05:00
successCases := [ ] api . Pod {
{ // Basic fields.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
2015-01-06 14:11:52 -05:00
Spec : api . PodSpec {
2015-03-03 17:48:55 -05:00
Volumes : [ ] api . Volume { { Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } } ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-26 12:52:50 -05:00
DNSPolicy : api . DNSClusterFirst ,
2014-08-26 14:25:17 -04:00
} ,
2014-07-22 14:45:12 -04:00
} ,
2015-01-06 14:11:52 -05:00
{ // Just about everything.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc.123.do-re-mi" , Namespace : "ns" } ,
2015-01-06 14:11:52 -05:00
Spec : api . PodSpec {
Volumes : [ ] api . Volume {
2015-03-03 17:48:55 -05:00
{ Name : "vol" , VolumeSource : api . VolumeSource { EmptyDir : & api . EmptyDirVolumeSource { } } } ,
2015-01-06 14:11:52 -05:00
} ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-06 14:11:52 -05:00
DNSPolicy : api . DNSClusterFirst ,
NodeSelector : map [ string ] string {
"key" : "value" ,
} ,
2015-05-22 19:40:57 -04:00
NodeName : "foobar" ,
2014-10-23 16:51:34 -04:00
} ,
2014-07-22 14:45:12 -04:00
} ,
2016-11-30 11:51:12 -05:00
{ // Serialized node affinity requirements.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-26 18:03:18 -05:00
Name : "123" ,
Namespace : "ns" ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec (
2016-11-30 11:51:12 -05:00
// TODO: Uncomment and move this block and move inside NodeAffinity once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// RequiredDuringSchedulingRequiredDuringExecution: &api.NodeSelector{
// NodeSelectorTerms: []api.NodeSelectorTerm{
// {
// MatchExpressions: []api.NodeSelectorRequirement{
// {
// Key: "key1",
// Operator: api.NodeSelectorOpExists
// },
// },
// },
// },
// },
2016-12-08 10:14:21 -05:00
& api . Affinity {
2016-11-30 11:51:12 -05:00
NodeAffinity : & api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "key2" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
} ,
} ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . PreferredSchedulingTerm {
{
Weight : 10 ,
Preference : api . NodeSelectorTerm {
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "foo" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "bar" } ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
2016-12-08 10:14:21 -05:00
) ,
2016-01-26 18:03:18 -05:00
} ,
2016-05-04 02:50:31 -04:00
{ // Serialized pod affinity in affinity requirements in annotations.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-05-04 02:50:31 -04:00
Name : "123" ,
Namespace : "ns" ,
// TODO: Uncomment and move this block into Annotations map once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// "requiredDuringSchedulingRequiredDuringExecution": [{
// "labelSelector": {
// "matchExpressions": [{
// "key": "key2",
// "operator": "In",
// "values": ["value1", "value2"]
// }]
// },
// "namespaces":["ns"],
// "topologyKey": "zone"
// }]
2016-12-08 10:14:21 -05:00
} ,
Spec : validPodSpec ( & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
TopologyKey : "zone" ,
Namespaces : [ ] string { "ns" } ,
} ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpNotIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
Namespaces : [ ] string { "ns" } ,
TopologyKey : "region" ,
} ,
} ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ) ,
2015-02-04 18:44:46 -05:00
} ,
2016-05-04 02:50:31 -04:00
{ // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-05-04 02:50:31 -04:00
Name : "123" ,
2015-01-24 09:36:22 -05:00
Namespace : "ns" ,
2016-05-04 02:50:31 -04:00
// TODO: Uncomment and move this block into Annotations map once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// "requiredDuringSchedulingRequiredDuringExecution": [{
// "labelSelector": {
// "matchExpressions": [{
// "key": "key2",
// "operator": "In",
// "values": ["value1", "value2"]
// }]
// },
// "namespaces":["ns"],
// "topologyKey": "zone"
// }]
2016-12-08 10:14:21 -05:00
} ,
Spec : validPodSpec ( & api . Affinity {
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpExists ,
} ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
TopologyKey : "zone" ,
Namespaces : [ ] string { "ns" } ,
} ,
} ,
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpDoesNotExist ,
} ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
Namespaces : [ ] string { "ns" } ,
TopologyKey : "region" ,
} ,
} ,
} ,
2015-01-24 09:36:22 -05:00
} ,
2016-12-08 10:14:21 -05:00
} ) ,
2015-01-24 09:36:22 -05:00
} ,
2017-01-04 06:45:50 -05:00
{ // populate forgiveness tolerations with exists operator in annotations.
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "Exists" , Value : "" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 60 } [ 0 ] } } ) ,
2017-01-04 06:45:50 -05:00
} ,
{ // populate forgiveness tolerations with equal operator in annotations.
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "Equal" , Value : "bar" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 60 } [ 0 ] } } ) ,
2017-01-04 06:45:50 -05:00
} ,
2016-03-30 23:42:57 -04:00
{ // populate tolerations equal operator in annotations.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "Equal" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
{ // populate tolerations exists operator in annotations.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "123" ,
Namespace : "ns" ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-03-30 23:42:57 -04:00
} ,
2017-01-04 06:45:50 -05:00
{ // empty key with Exists operator is OK for toleration, empty toleration key means match all taint keys.
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Operator : "Exists" , Effect : "NoSchedule" } } ) ,
2017-01-04 06:45:50 -05:00
} ,
{ // empty operator is OK for toleration, defaults to Equal.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
2017-01-04 06:45:50 -05:00
{ // empty effect is OK for toleration, empty toleration effect means match all taint effects.
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "123" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "Equal" , Value : "bar" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
2017-01-04 06:45:50 -05:00
{ // negative tolerationSeconds is OK for toleration.
ObjectMeta : metav1 . ObjectMeta {
Name : "pod-forgiveness-invalid" ,
Namespace : "ns" ,
} ,
2017-02-07 09:08:45 -05:00
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "node.alpha.kubernetes.io/notReady" , Operator : "Exists" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { - 2 } [ 0 ] } } ) ,
2017-01-04 06:45:50 -05:00
} ,
2016-06-10 04:02:36 -04:00
{ // docker default seccomp profile
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-06-10 04:02:36 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "docker/default" ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
{ // unconfined seccomp profile
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-06-10 04:02:36 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "unconfined" ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
{ // localhost seccomp profile
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-06-10 04:02:36 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "localhost/foo" ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
{ // localhost seccomp profile for a container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-06-10 04:02:36 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompContainerAnnotationKeyPrefix + "foo" : "localhost/foo" ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-08-16 19:41:05 -04:00
} ,
{ // default AppArmor profile for a container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-16 19:41:05 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "ctr" : apparmor . ProfileRuntimeDefault ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-08-16 19:41:05 -04:00
} ,
{ // default AppArmor profile for an init container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-16 19:41:05 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "init-ctr" : apparmor . ProfileRuntimeDefault ,
} ,
} ,
2016-06-10 04:02:36 -04:00
Spec : api . PodSpec {
2016-12-21 23:54:12 -05:00
InitContainers : [ ] api . Container { { Name : "init-ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-08-16 19:41:05 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
} ,
{ // localhost AppArmor profile for a container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-16 19:41:05 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "ctr" : apparmor . ProfileNamePrefix + "foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
2016-06-14 09:09:53 -04:00
{ // syntactically valid sysctls
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-06-14 09:09:53 -04:00
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SysctlsPodAnnotationKey : "kernel.shmmni=32768,kernel.shmmax=1000000000" ,
api . UnsafeSysctlsPodAnnotationKey : "knet.ipv4.route.min_pmtu=1000" ,
} ,
} ,
2016-12-08 10:14:21 -05:00
Spec : validPodSpec ( nil ) ,
2016-06-14 09:09:53 -04:00
} ,
2016-09-26 11:11:31 -04:00
{ // valid opaque integer resources for init container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "valid-opaque-int" , Namespace : "ns" } ,
2016-09-26 11:11:31 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Name : "valid-opaque-int" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "10" ) ,
2016-09-26 11:11:31 -04:00
} ,
Limits : api . ResourceList {
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "20" ) ,
2016-09-26 11:11:31 -04:00
} ,
} ,
2016-12-21 23:54:12 -05:00
TerminationMessagePolicy : "File" ,
2016-09-26 11:11:31 -04:00
} ,
} ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-09-26 11:11:31 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
} ,
{ // valid opaque integer resources for regular container
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "valid-opaque-int" , Namespace : "ns" } ,
2016-09-26 11:11:31 -04:00
Spec : api . PodSpec {
2016-12-21 23:54:12 -05:00
InitContainers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-09-26 11:11:31 -04:00
Containers : [ ] api . Container {
{
Name : "valid-opaque-int" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "10" ) ,
2016-09-26 11:11:31 -04:00
} ,
Limits : api . ResourceList {
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "20" ) ,
2016-09-26 11:11:31 -04:00
} ,
} ,
2016-12-21 23:54:12 -05:00
TerminationMessagePolicy : "File" ,
2016-09-26 11:11:31 -04:00
} ,
} ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
} ,
2016-05-04 02:50:31 -04:00
}
for _ , pod := range successCases {
if errs := ValidatePod ( & pod ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
}
2017-05-13 15:53:57 -04:00
errorCases := map [ string ] struct {
spec api . Pod
expectedError string
} {
2016-03-30 23:42:57 -04:00
"bad name" : {
2017-05-13 15:53:57 -04:00
expectedError : "metadata.name" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : "ns" } ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
} ,
2016-03-30 23:42:57 -04:00
} ,
} ,
2017-06-14 19:52:31 -04:00
"image whitespace" : {
expectedError : "spec.containers[0].image" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "ns" } ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Containers : [ ] api . Container { { Name : "ctr" , Image : " " , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
} ,
} ,
} ,
"image leading and trailing whitespace" : {
expectedError : "spec.containers[0].image" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "ns" } ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Containers : [ ] api . Container { { Name : "ctr" , Image : " something " , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
} ,
} ,
} ,
2016-03-30 23:42:57 -04:00
"bad namespace" : {
2017-05-13 15:53:57 -04:00
expectedError : "metadata.namespace" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "" } ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
} ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"bad spec" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.containers[0].name" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "ns" } ,
Spec : api . PodSpec {
Containers : [ ] api . Container { { } } ,
} ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"bad label" : {
2017-05-13 15:53:57 -04:00
expectedError : "NoUppercaseOrSpecialCharsLike=Equals" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "abc" ,
Namespace : "ns" ,
Labels : map [ string ] string {
"NoUppercaseOrSpecialCharsLike=Equals" : "bar" ,
} ,
} ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
} ,
2016-11-30 11:51:12 -05:00
"invalid node selector requirement in node affinity, operator can't be null" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.affinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
NodeAffinity : & api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "key1" ,
} ,
2016-11-30 11:51:12 -05:00
} ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-01-26 18:03:18 -05:00
} ,
2016-11-30 11:51:12 -05:00
"invalid preferredSchedulingTerm in node affinity, weight should be in range 1-100" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be in the range 1-100" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
NodeAffinity : & api . NodeAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . PreferredSchedulingTerm {
{
Weight : 199 ,
Preference : api . NodeSelectorTerm {
MatchExpressions : [ ] api . NodeSelectorRequirement {
{
Key : "foo" ,
Operator : api . NodeSelectorOpIn ,
Values : [ ] string { "bar" } ,
} ,
2016-11-30 11:51:12 -05:00
} ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-01-26 18:03:18 -05:00
} ,
2016-02-11 02:06:33 -05:00
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.affinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
2016-01-26 18:03:18 -05:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( & api . Affinity {
NodeAffinity : & api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm { } ,
} ,
} ,
} ) ,
} ,
2016-01-26 18:03:18 -05:00
} ,
2016-02-11 02:06:33 -05:00
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.affinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
NodeAffinity : & api . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & api . NodeSelector {
NodeSelectorTerms : [ ] api . NodeSelectorTerm {
{
MatchExpressions : [ ] api . NodeSelectorRequirement { } ,
} ,
2016-11-30 11:51:12 -05:00
} ,
2016-01-26 18:03:18 -05:00
} ,
2016-11-30 11:51:12 -05:00
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-01-26 18:03:18 -05:00
} ,
2016-05-04 02:50:31 -04:00
"invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be in the range 1-100" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
PodAffinity : & api . PodAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 109 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpNotIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
Namespaces : [ ] string { "ns" } ,
TopologyKey : "region" ,
2016-12-08 10:14:21 -05:00
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
"invalid labelSelector in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, values should be empty if the operator is Exists" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.matchExpressions.matchExpressions[0].values" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
PodAntiAffinity : & api . PodAntiAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpExists ,
Values : [ ] string { "value1" , "value2" } ,
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
Namespaces : [ ] string { "ns" } ,
TopologyKey : "region" ,
2016-12-08 10:14:21 -05:00
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
"invalid name space in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, name space shouldbe valid" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].podAffinityTerm.namespace" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
PodAffinity : & api . PodAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpExists ,
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
Namespaces : [ ] string { "INVALID_NAMESPACE" } ,
TopologyKey : "region" ,
2016-12-08 10:14:21 -05:00
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2017-08-01 15:03:51 -04:00
"invalid hard pod affinity, empty topologyKey is not allowed for hard pod affinity" : {
expectedError : "can not be empty" ,
2017-05-13 15:53:57 -04:00
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
PodAffinity : & api . PodAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
Namespaces : [ ] string { "ns" } ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2017-08-01 15:03:51 -04:00
"invalid hard pod anti-affinity, empty topologyKey is not allowed for hard pod anti-affinity" : {
expectedError : "can not be empty" ,
2017-05-13 15:53:57 -04:00
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
2016-05-04 02:50:31 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( & api . Affinity {
PodAntiAffinity : & api . PodAntiAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : [ ] api . PodAffinityTerm {
{
2016-12-08 10:14:21 -05:00
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
2017-05-13 15:53:57 -04:00
Operator : metav1 . LabelSelectorOpIn ,
2016-12-08 10:14:21 -05:00
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
} ,
Namespaces : [ ] string { "ns" } ,
2016-05-04 02:50:31 -04:00
} ,
2016-12-08 10:14:21 -05:00
} ,
} ,
2017-05-13 15:53:57 -04:00
} ) ,
} ,
} ,
2017-08-01 15:03:51 -04:00
"invalid soft pod affinity, empty topologyKey is not allowed for soft pod affinity" : {
expectedError : "can not be empty" ,
2017-05-13 15:53:57 -04:00
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
2016-05-04 02:50:31 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( & api . Affinity {
PodAffinity : & api . PodAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpNotIn ,
Values : [ ] string { "value1" , "value2" } ,
} ,
} ,
} ,
Namespaces : [ ] string { "ns" } ,
} ,
} ,
} ,
} ,
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2017-08-01 15:03:51 -04:00
"invalid soft pod anti-affinity, empty topologyKey is not allowed for soft pod anti-affinity" : {
expectedError : "can not be empty" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : validPodSpec ( & api . Affinity {
PodAntiAffinity : & api . PodAntiAffinity {
PreferredDuringSchedulingIgnoredDuringExecution : [ ] api . WeightedPodAffinityTerm {
{
Weight : 10 ,
PodAffinityTerm : api . PodAffinityTerm {
LabelSelector : & metav1 . LabelSelector {
MatchExpressions : [ ] metav1 . LabelSelectorRequirement {
{
Key : "key2" ,
Operator : metav1 . LabelSelectorOpNotIn ,
Values : [ ] string { "value1" , "value2" } ,
2017-05-13 15:53:57 -04:00
} ,
} ,
} ,
Namespaces : [ ] string { "ns" } ,
} ,
} ,
} ,
} ,
} ) ,
} ,
2016-05-04 02:50:31 -04:00
} ,
2016-03-30 23:42:57 -04:00
"invalid toleration key" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.tolerations[0].key" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "nospecialchars^=@" , Operator : "Equal" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"invalid toleration operator" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.tolerations[0].operator" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "In" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"value must be empty when `operator` is 'Exists'" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.tolerations[0].operator" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "foo" , Operator : "Exists" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2016-03-30 23:42:57 -04:00
} ,
} ,
2017-01-04 06:45:50 -05:00
"operator must be 'Exists' when `key` is empty" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.tolerations[0].operator" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
} ,
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Operator : "Equal" , Value : "bar" , Effect : "NoSchedule" } } ) ,
2017-01-04 06:45:50 -05:00
} ,
} ,
"effect must be 'NoExecute' when `TolerationSeconds` is set" : {
2017-05-13 15:53:57 -04:00
expectedError : "spec.tolerations[0].effect" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "pod-forgiveness-invalid" ,
Namespace : "ns" ,
} ,
Spec : extendPodSpecwithTolerations ( validPodSpec ( nil ) , [ ] api . Toleration { { Key : "node.alpha.kubernetes.io/notReady" , Operator : "Exists" , Effect : "NoSchedule" , TolerationSeconds : & [ ] int64 { 20 } [ 0 ] } } ) ,
2017-01-04 06:45:50 -05:00
} ,
} ,
2016-06-10 04:02:36 -04:00
"must be a valid pod seccomp profile" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be a valid seccomp profile" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
"must be a valid container seccomp profile" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be a valid seccomp profile" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompContainerAnnotationKeyPrefix + "foo" : "foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
"must be a non-empty container name in seccomp annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "name part must be non-empty" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompContainerAnnotationKeyPrefix : "foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
"must be a non-empty container profile in seccomp annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be a valid seccomp profile" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompContainerAnnotationKeyPrefix + "foo" : "" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
"must be a relative path in a node-local seccomp profile annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be a relative path" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "localhost//foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
"must not start with '../'" : {
2017-05-13 15:53:57 -04:00
expectedError : "must not contain '..'" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SeccompPodAnnotationKey : "localhost/../foo" ,
} ,
2016-06-10 04:02:36 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
2016-08-16 19:41:05 -04:00
} ,
"AppArmor profile must apply to a container" : {
2017-05-13 15:53:57 -04:00
expectedError : "metadata.annotations[container.apparmor.security.beta.kubernetes.io/fake-ctr]" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "ctr" : apparmor . ProfileRuntimeDefault ,
apparmor . ContainerAnnotationKeyPrefix + "init-ctr" : apparmor . ProfileRuntimeDefault ,
apparmor . ContainerAnnotationKeyPrefix + "fake-ctr" : apparmor . ProfileRuntimeDefault ,
} ,
} ,
Spec : api . PodSpec {
InitContainers : [ ] api . Container { { Name : "init-ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-08-16 19:41:05 -04:00
} ,
} ,
} ,
"AppArmor profile format must be valid" : {
2017-05-13 15:53:57 -04:00
expectedError : "invalid AppArmor profile name" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "ctr" : "bad-name" ,
} ,
2016-08-16 19:41:05 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-08-16 19:41:05 -04:00
} ,
} ,
"only default AppArmor profile may start with runtime/" : {
2017-05-13 15:53:57 -04:00
expectedError : "invalid AppArmor profile name" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
apparmor . ContainerAnnotationKeyPrefix + "ctr" : "runtime/foo" ,
} ,
2016-08-16 19:41:05 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-10 04:02:36 -04:00
} ,
} ,
2016-06-14 09:09:53 -04:00
"invalid sysctl annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "metadata.annotations[security.alpha.kubernetes.io/sysctls]" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SysctlsPodAnnotationKey : "foo:" ,
} ,
2016-06-14 09:09:53 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-14 09:09:53 -04:00
} ,
} ,
"invalid comma-separated sysctl annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "not of the format sysctl_name=value" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SysctlsPodAnnotationKey : "kernel.msgmax," ,
} ,
2016-06-14 09:09:53 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-14 09:09:53 -04:00
} ,
} ,
"invalid unsafe sysctl annotation" : {
2017-05-13 15:53:57 -04:00
expectedError : "metadata.annotations[security.alpha.kubernetes.io/sysctls]" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SysctlsPodAnnotationKey : "foo:" ,
} ,
2016-06-14 09:09:53 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-14 09:09:53 -04:00
} ,
} ,
"intersecting safe sysctls and unsafe sysctls annotations" : {
2017-05-13 15:53:57 -04:00
expectedError : "can not be safe and unsafe" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "123" ,
Namespace : "ns" ,
Annotations : map [ string ] string {
api . SysctlsPodAnnotationKey : "kernel.shmmax=10000000" ,
api . UnsafeSysctlsPodAnnotationKey : "kernel.shmmax=10000000" ,
} ,
2016-06-14 09:09:53 -04:00
} ,
2017-05-13 15:53:57 -04:00
Spec : validPodSpec ( nil ) ,
2016-06-14 09:09:53 -04:00
} ,
} ,
2016-09-26 11:11:31 -04:00
"invalid opaque integer resource requirement: request must be <= limit" : {
2017-08-09 23:45:09 -04:00
expectedError : "must be less than or equal to pod.alpha.kubernetes.io/opaque-int-resource-A" ,
2017-05-13 15:53:57 -04:00
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Name : "invalid" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "2" ) ,
} ,
Limits : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "1" ) ,
} ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
"invalid fractional opaque integer resource in container request" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be an integer" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Name : "invalid" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "500m" ) ,
} ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
"invalid fractional opaque integer resource in init container request" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be an integer" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Name : "invalid" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "500m" ) ,
} ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
"invalid fractional opaque integer resource in container limit" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be an integer" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Name : "invalid" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "5" ) ,
} ,
Limits : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "2.5" ) ,
} ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
"invalid fractional opaque integer resource in init container limit" : {
2017-05-13 15:53:57 -04:00
expectedError : "must be an integer" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" } ,
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Name : "invalid" ,
Image : "image" ,
ImagePullPolicy : "IfNotPresent" ,
Resources : api . ResourceRequirements {
Requests : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "5" ) ,
} ,
Limits : api . ResourceList {
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "2.5" ) ,
} ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2017-05-13 15:48:44 -04:00
"mirror-pod present without nodeName" : {
2017-05-13 15:53:57 -04:00
expectedError : "mirror" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "" } } ,
Spec : api . PodSpec {
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-05-13 15:48:44 -04:00
} ,
} ,
"mirror-pod populated without nodeName" : {
2017-05-13 15:53:57 -04:00
expectedError : "mirror" ,
spec : api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "123" , Namespace : "ns" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "foo" } } ,
Spec : api . PodSpec {
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} ,
2015-01-06 14:11:52 -05:00
}
for k , v := range errorCases {
2017-05-13 15:53:57 -04:00
if errs := ValidatePod ( & v . spec ) ; len ( errs ) == 0 {
2015-03-18 11:00:18 -04:00
t . Errorf ( "expected failure for %q" , k )
2017-05-13 15:53:57 -04:00
} else if v . expectedError == "" {
t . Errorf ( "missing expectedError for %q, got %q" , k , errs . ToAggregate ( ) . Error ( ) )
} else if actualError := errs . ToAggregate ( ) . Error ( ) ; ! strings . Contains ( actualError , v . expectedError ) {
t . Errorf ( "expected error for %q to contain %q, got %q" , k , v . expectedError , actualError )
2015-01-06 14:11:52 -05:00
}
2014-10-23 16:14:13 -04:00
}
2014-07-22 14:45:12 -04:00
}
2014-10-09 23:30:34 -04:00
func TestValidatePodUpdate ( t * testing . T ) {
2016-01-26 15:52:14 -05:00
var (
activeDeadlineSecondsZero = int64 ( 0 )
activeDeadlineSecondsNegative = int64 ( - 30 )
activeDeadlineSecondsPositive = int64 ( 30 )
activeDeadlineSecondsLarger = int64 ( 31 )
2016-12-03 13:57:26 -05:00
now = metav1 . Now ( )
2016-01-26 15:52:14 -05:00
grace = int64 ( 30 )
grace2 = int64 ( 31 )
)
2014-10-09 23:30:34 -04:00
tests := [ ] struct {
2017-05-13 15:53:57 -04:00
a api . Pod
b api . Pod
err string
test string
2014-10-09 23:30:34 -04:00
} {
2017-05-13 15:53:57 -04:00
{ api . Pod { } , api . Pod { } , "" , "nothing" } ,
2014-10-09 23:30:34 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-10-09 23:30:34 -04:00
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "bar" } ,
2014-10-09 23:30:34 -04:00
} ,
2017-05-13 15:53:57 -04:00
"metadata.name" ,
2014-10-09 23:30:34 -04:00
"ids" ,
} ,
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-23 16:51:34 -04:00
Name : "foo" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
2014-10-09 23:30:34 -04:00
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-23 16:51:34 -04:00
Name : "foo" ,
Labels : map [ string ] string {
"bar" : "foo" ,
} ,
2014-10-09 23:30:34 -04:00
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2014-10-09 23:30:34 -04:00
"labels" ,
} ,
2015-01-24 09:36:22 -05:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-01-24 09:36:22 -05:00
Name : "foo" ,
Annotations : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-01-24 09:36:22 -05:00
Name : "foo" ,
Annotations : map [ string ] string {
"bar" : "foo" ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2015-01-24 09:36:22 -05:00
"annotations" ,
} ,
2014-10-09 23:30:34 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-23 16:51:34 -04:00
Name : "foo" ,
} ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V1" ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V2" ,
} ,
{
Image : "bar:V2" ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"may not add or remove containers" ,
2014-10-09 23:30:34 -04:00
"more containers" ,
} ,
2016-07-04 16:30:54 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-07-04 16:30:54 -04:00
Name : "foo" ,
} ,
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Image : "foo:V1" ,
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-07-04 16:30:54 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Image : "foo:V2" ,
} ,
{
Image : "bar:V2" ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"may not add or remove containers" ,
2016-07-04 16:30:54 -04:00
"more init containers" ,
} ,
2014-10-09 23:30:34 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2015-08-19 19:59:43 -04:00
Spec : api . PodSpec { Containers : [ ] api . Container { { Image : "foo:V1" } } } ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , DeletionTimestamp : & now } ,
2015-08-19 19:59:43 -04:00
Spec : api . PodSpec { Containers : [ ] api . Container { { Image : "foo:V1" } } } ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2015-08-19 19:59:43 -04:00
"deletion timestamp filled out" ,
} ,
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , DeletionTimestamp : & now , DeletionGracePeriodSeconds : & grace } ,
2015-08-19 19:59:43 -04:00
Spec : api . PodSpec { Containers : [ ] api . Container { { Image : "foo:V1" } } } ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , DeletionTimestamp : & now , DeletionGracePeriodSeconds : & grace2 } ,
2015-08-19 19:59:43 -04:00
Spec : api . PodSpec { Containers : [ ] api . Container { { Image : "foo:V1" } } } ,
} ,
2017-05-13 15:53:57 -04:00
"metadata.deletionGracePeriodSeconds" ,
2015-08-19 19:59:43 -04:00
"deletion grace period seconds cleared" ,
} ,
2014-10-09 23:30:34 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V1" ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V2" ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2014-10-09 23:30:34 -04:00
"image change" ,
} ,
2016-07-04 16:30:54 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-07-04 16:30:54 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Image : "foo:V1" ,
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-07-04 16:30:54 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Image : "foo:V2" ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2016-07-04 16:30:54 -04:00
"init container image change" ,
} ,
2016-01-26 15:52:14 -05:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-01-26 15:52:14 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{ } ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-01-26 15:52:14 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V2" ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.containers[0].image" ,
2016-01-26 15:52:14 -05:00
"image change to empty" ,
} ,
2016-07-04 16:30:54 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-07-04 16:30:54 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{ } ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2016-07-04 16:30:54 -04:00
Spec : api . PodSpec {
InitContainers : [ ] api . Container {
{
Image : "foo:V2" ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.initContainers[0].image" ,
2016-07-04 16:30:54 -04:00
"init container image change to empty" ,
} ,
2016-01-26 15:52:14 -05:00
{
api . Pod {
Spec : api . PodSpec { } ,
} ,
api . Pod {
Spec : api . PodSpec { } ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds no change, nil" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds no change, set" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
api . Pod { } ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to positive from nil" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsLarger ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to smaller positive" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsLarger ,
} ,
} ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.activeDeadlineSeconds" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to larger positive" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsNegative ,
} ,
} ,
api . Pod { } ,
2017-05-13 15:53:57 -04:00
"spec.activeDeadlineSeconds" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to negative from nil" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsNegative ,
} ,
} ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.activeDeadlineSeconds" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to negative from positive" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsZero ,
} ,
} ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to zero from positive" ,
} ,
{
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsZero ,
} ,
} ,
api . Pod { } ,
2017-05-13 15:53:57 -04:00
"" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to zero from nil" ,
} ,
{
api . Pod { } ,
api . Pod {
Spec : api . PodSpec {
ActiveDeadlineSeconds : & activeDeadlineSecondsPositive ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.activeDeadlineSeconds" ,
2016-01-26 15:52:14 -05:00
"activeDeadlineSeconds change to nil from positive" ,
} ,
2014-10-09 23:30:34 -04:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V1" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : getResourceLimits ( "100m" , "0" ) ,
} ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V2" ,
2015-02-09 17:44:32 -05:00
Resources : api . ResourceRequirements {
2015-01-24 23:19:36 -05:00
Limits : getResourceLimits ( "1000m" , "0" ) ,
} ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec: Forbidden: pod updates may not change fields" ,
2014-10-09 23:30:34 -04:00
"cpu change" ,
} ,
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V1" ,
2015-02-23 17:25:56 -05:00
Ports : [ ] api . ContainerPort {
2014-11-13 10:52:13 -05:00
{ HostPort : 8080 , ContainerPort : 80 } ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } ,
2014-11-13 10:52:13 -05:00
Spec : api . PodSpec {
Containers : [ ] api . Container {
{
Image : "foo:V2" ,
2015-02-23 17:25:56 -05:00
Ports : [ ] api . ContainerPort {
2014-11-13 10:52:13 -05:00
{ HostPort : 8000 , ContainerPort : 80 } ,
2014-10-09 23:30:34 -04:00
} ,
} ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec: Forbidden: pod updates may not change fields" ,
2014-10-09 23:30:34 -04:00
"port change" ,
} ,
2015-02-04 17:21:34 -05:00
{
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 17:21:34 -05:00
Name : "foo" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
} ,
api . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 17:21:34 -05:00
Name : "foo" ,
Labels : map [ string ] string {
"Bar" : "foo" ,
} ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2015-02-04 17:21:34 -05:00
"bad label change" ,
} ,
2017-02-21 13:03:13 -05:00
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value2" } } ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" } } ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.tolerations: Forbidden" ,
2017-02-21 13:03:13 -05:00
"existing toleration value modified in pod spec updates" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value2" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : nil } } ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 10 } [ 0 ] } } ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.tolerations: Forbidden" ,
2017-02-21 13:03:13 -05:00
"existing toleration value modified in pod spec updates with modified tolerationSeconds" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 10 } [ 0 ] } } ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 20 } [ 0 ] } } ,
} } ,
2017-05-13 15:53:57 -04:00
"" ,
2017-02-21 13:03:13 -05:00
"modified tolerationSeconds in existing toleration value in pod spec updates" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value2" } } ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" } } ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"spec.tolerations: Forbidden" ,
2017-02-21 13:03:13 -05:00
"toleration modified in updates to an unscheduled pod" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" } } ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" } } ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2017-02-21 13:03:13 -05:00
"tolerations unmodified in updates to a scheduled pod" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration {
{ Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 20 } [ 0 ] } ,
{ Key : "key2" , Value : "value2" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 30 } [ 0 ] } ,
} ,
} } ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 10 } [ 0 ] } } ,
} ,
} ,
2017-05-13 15:53:57 -04:00
"" ,
2017-02-21 13:03:13 -05:00
"added valid new toleration to existing tolerations in pod spec updates" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" } , Spec : api . PodSpec {
NodeName : "node1" ,
Tolerations : [ ] api . Toleration {
{ Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 20 } [ 0 ] } ,
{ Key : "key2" , Value : "value2" , Operator : "Equal" , Effect : "NoSchedule" , TolerationSeconds : & [ ] int64 { 30 } [ 0 ] } ,
} ,
} } ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" , Tolerations : [ ] api . Toleration { { Key : "key1" , Value : "value1" , Operator : "Equal" , Effect : "NoExecute" , TolerationSeconds : & [ ] int64 { 10 } [ 0 ] } } ,
} } ,
2017-05-13 15:53:57 -04:00
"spec.tolerations[1].effect" ,
2017-02-21 13:03:13 -05:00
"added invalid new toleration to existing tolerations in pod spec updates" ,
} ,
2017-05-13 15:48:44 -04:00
{
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" } , Spec : api . PodSpec { NodeName : "foo" } } ,
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" } } ,
2017-05-13 15:53:57 -04:00
"spec: Forbidden: pod updates may not change fields" ,
2017-05-13 15:48:44 -04:00
"removed nodeName from pod spec" ,
} ,
{
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "" } } , Spec : api . PodSpec { NodeName : "foo" } } ,
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" } , Spec : api . PodSpec { NodeName : "foo" } } ,
2017-05-13 15:53:57 -04:00
"metadata.annotations[kubernetes.io/config.mirror]" ,
2017-05-13 15:48:44 -04:00
"removed mirror pod annotation" ,
} ,
{
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" } , Spec : api . PodSpec { NodeName : "foo" } } ,
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "" } } , Spec : api . PodSpec { NodeName : "foo" } } ,
2017-05-13 15:53:57 -04:00
"metadata.annotations[kubernetes.io/config.mirror]" ,
2017-05-13 15:48:44 -04:00
"added mirror pod annotation" ,
} ,
{
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "foo" } } , Spec : api . PodSpec { NodeName : "foo" } } ,
api . Pod { ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Annotations : map [ string ] string { api . MirrorPodAnnotationKey : "bar" } } , Spec : api . PodSpec { NodeName : "foo" } } ,
2017-05-13 15:53:57 -04:00
"metadata.annotations[kubernetes.io/config.mirror]" ,
2017-05-13 15:48:44 -04:00
"changed mirror pod annotation" ,
} ,
2017-05-09 21:25:34 -04:00
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
PriorityClassName : "bar-priority" ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
PriorityClassName : "foo-priority" ,
} ,
} ,
"spec: Forbidden: pod updates" ,
"changed priority class name" ,
} ,
{
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
PriorityClassName : "" ,
} ,
} ,
api . Pod {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
} ,
Spec : api . PodSpec {
NodeName : "node1" ,
PriorityClassName : "foo-priority" ,
} ,
} ,
"spec: Forbidden: pod updates" ,
"removed priority class name" ,
} ,
2014-10-09 23:30:34 -04:00
}
for _ , test := range tests {
2015-03-19 20:51:07 -04:00
test . a . ObjectMeta . ResourceVersion = "1"
test . b . ObjectMeta . ResourceVersion = "1"
2014-10-09 23:30:34 -04:00
errs := ValidatePodUpdate ( & test . a , & test . b )
2017-05-13 15:53:57 -04:00
if test . err == "" {
2014-10-09 23:30:34 -04:00
if len ( errs ) != 0 {
2016-01-26 15:52:14 -05:00
t . Errorf ( "unexpected invalid: %s (%+v)\nA: %+v\nB: %+v" , test . test , errs , test . a , test . b )
2014-10-09 23:30:34 -04:00
}
} else {
if len ( errs ) == 0 {
2016-01-26 15:52:14 -05:00
t . Errorf ( "unexpected valid: %s\nA: %+v\nB: %+v" , test . test , test . a , test . b )
2017-05-13 15:53:57 -04:00
} else if actualErr := errs . ToAggregate ( ) . Error ( ) ; ! strings . Contains ( actualErr , test . err ) {
t . Errorf ( "unexpected error message: %s\nExpected error: %s\nActual error: %s" , test . test , test . err , actualErr )
2014-10-09 23:30:34 -04:00
}
}
}
}
2015-03-31 14:13:44 -04:00
func makeValidService ( ) api . Service {
return api . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 14:13:44 -04:00
Name : "valid" ,
Namespace : "valid" ,
Labels : map [ string ] string { } ,
Annotations : map [ string ] string { } ,
ResourceVersion : "1" ,
} ,
Spec : api . ServiceSpec {
Selector : map [ string ] string { "key" : "val" } ,
SessionAffinity : "None" ,
2015-05-22 17:49:26 -04:00
Type : api . ServiceTypeClusterIP ,
2015-11-10 01:28:45 -05:00
Ports : [ ] api . ServicePort { { Name : "p" , Protocol : "TCP" , Port : 8675 , TargetPort : intstr . FromInt ( 8675 ) } } ,
2015-03-31 14:13:44 -04:00
} ,
}
}
2014-07-10 15:45:01 -04:00
func TestValidateService ( t * testing . T ) {
2014-09-10 12:41:31 -04:00
testCases := [ ] struct {
2015-03-31 14:13:44 -04:00
name string
tweakSvc func ( svc * api . Service ) // given a basic valid service, each test case can customize it
numErrs int
2014-09-10 12:41:31 -04:00
} {
{
2015-03-08 00:40:18 -05:00
name : "missing namespace" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Namespace = ""
2015-01-26 12:52:50 -05:00
} ,
numErrs : 1 ,
} ,
{
2015-03-08 00:40:18 -05:00
name : "invalid namespace" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Namespace = "-123"
2015-01-26 12:52:50 -05:00
} ,
numErrs : 1 ,
} ,
{
2015-03-08 00:40:18 -05:00
name : "missing name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Name = ""
2014-09-10 12:41:31 -04:00
} ,
numErrs : 1 ,
2014-07-10 15:45:01 -04:00
} ,
2014-09-29 17:18:18 -04:00
{
2015-03-08 00:40:18 -05:00
name : "invalid name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Name = "-123"
2014-09-29 17:18:18 -04:00
} ,
numErrs : 1 ,
} ,
2014-09-10 12:41:31 -04:00
{
2015-03-08 00:40:18 -05:00
name : "too long name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2016-07-24 13:01:38 -04:00
s . Name = strings . Repeat ( "a" , 64 )
2014-09-10 12:41:31 -04:00
} ,
numErrs : 1 ,
2014-08-22 17:44:21 -04:00
} ,
2015-01-27 18:56:38 -05:00
{
2015-03-08 00:40:18 -05:00
name : "invalid generateName" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . GenerateName = "-123"
2015-01-27 18:56:38 -05:00
} ,
numErrs : 1 ,
} ,
{
2015-03-08 00:40:18 -05:00
name : "too long generateName" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2016-07-24 13:01:38 -04:00
s . GenerateName = strings . Repeat ( "a" , 64 )
2015-01-27 18:56:38 -05:00
} ,
numErrs : 1 ,
} ,
2014-09-10 12:41:31 -04:00
{
2015-03-08 00:40:18 -05:00
name : "invalid label" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Labels [ "NoUppercaseOrSpecialCharsLike=Equals" ] = "bar"
2014-09-10 12:41:31 -04:00
} ,
numErrs : 1 ,
} ,
{
2015-03-08 00:40:18 -05:00
name : "invalid annotation" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Annotations [ "NoSpecialCharsLike=Equals" ] = "bar"
2014-09-10 12:41:31 -04:00
} ,
numErrs : 1 ,
} ,
2015-03-13 11:16:41 -04:00
{
name : "nil selector" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Selector = nil
} ,
numErrs : 0 ,
} ,
2014-09-10 12:53:40 -04:00
{
2015-03-08 00:40:18 -05:00
name : "invalid selector" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Spec . Selector [ "NoSpecialCharsLike=Equals" ] = "bar"
2014-09-10 12:53:40 -04:00
} ,
numErrs : 1 ,
} ,
2014-09-10 12:41:31 -04:00
{
2015-03-08 00:40:18 -05:00
name : "missing session affinity" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
s . Spec . SessionAffinity = ""
2014-09-10 12:41:31 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
2014-09-10 12:41:31 -04:00
} ,
2015-05-22 17:49:26 -04:00
{
name : "missing type" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = ""
} ,
numErrs : 1 ,
} ,
2015-03-13 11:16:41 -04:00
{
name : "missing ports" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports = nil
} ,
numErrs : 1 ,
} ,
2015-11-06 12:53:57 -05:00
{
name : "missing ports but headless" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Ports = nil
s . Spec . ClusterIP = api . ClusterIPNone
} ,
numErrs : 0 ,
} ,
2015-03-13 11:16:41 -04:00
{
name : "empty port[0] name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Name = ""
} ,
numErrs : 0 ,
} ,
{
name : "empty port[1] name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "" , Protocol : "TCP" , Port : 12345 , TargetPort : intstr . FromInt ( 12345 ) } )
2015-03-13 11:16:41 -04:00
} ,
numErrs : 1 ,
} ,
2015-05-05 14:51:51 -04:00
{
name : "empty multi-port port[0] name" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Ports [ 0 ] . Name = ""
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "p" , Protocol : "TCP" , Port : 12345 , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-05 14:51:51 -04:00
} ,
numErrs : 1 ,
} ,
2015-03-13 11:16:41 -04:00
{
name : "invalid port name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Name = "INVALID"
} ,
numErrs : 1 ,
} ,
2014-09-10 12:41:31 -04:00
{
2015-03-08 00:40:18 -05:00
name : "missing protocol" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Protocol = ""
2014-09-10 12:41:31 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
2014-09-10 12:41:31 -04:00
} ,
{
2015-03-08 00:40:18 -05:00
name : "invalid protocol" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Protocol = "INVALID"
2014-09-10 12:41:31 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
2014-09-10 12:41:31 -04:00
} ,
2015-03-16 17:36:30 -04:00
{
2015-05-23 16:41:11 -04:00
name : "invalid cluster ip" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-23 16:41:11 -04:00
s . Spec . ClusterIP = "invalid"
2015-03-16 17:36:30 -04:00
} ,
numErrs : 1 ,
} ,
2014-09-10 12:41:31 -04:00
{
2015-03-08 00:40:18 -05:00
name : "missing port" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Port = 0
2014-09-10 12:41:31 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
2014-07-10 15:45:01 -04:00
} ,
2014-10-31 02:03:52 -04:00
{
2015-03-08 00:40:18 -05:00
name : "invalid port" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Port = 65536
2014-10-31 02:03:52 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
2014-10-31 02:03:52 -04:00
} ,
{
2015-03-13 11:16:41 -04:00
name : "invalid TargetPort int" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-11-10 01:28:45 -05:00
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromInt ( 65536 )
2014-10-31 02:03:52 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 1 ,
} ,
2015-11-27 03:09:13 -05:00
{
name : "valid port headless" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Ports [ 0 ] . Port = 11722
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromInt ( 11722 )
s . Spec . ClusterIP = api . ClusterIPNone
} ,
numErrs : 0 ,
} ,
{
2016-02-22 23:06:16 -05:00
name : "invalid port headless 1" ,
2015-11-27 03:09:13 -05:00
tweakSvc : func ( s * api . Service ) {
s . Spec . Ports [ 0 ] . Port = 11722
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromInt ( 11721 )
s . Spec . ClusterIP = api . ClusterIPNone
} ,
2016-02-22 23:06:16 -05:00
// in the v1 API, targetPorts on headless services were tolerated.
// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
// numErrs: 1,
numErrs : 0 ,
2015-11-27 03:09:13 -05:00
} ,
{
2016-02-22 23:06:16 -05:00
name : "invalid port headless 2" ,
2015-11-27 03:09:13 -05:00
tweakSvc : func ( s * api . Service ) {
s . Spec . Ports [ 0 ] . Port = 11722
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromString ( "target" )
s . Spec . ClusterIP = api . ClusterIPNone
} ,
2016-02-22 23:06:16 -05:00
// in the v1 API, targetPorts on headless services were tolerated.
// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
// numErrs: 1,
numErrs : 0 ,
2015-11-27 03:09:13 -05:00
} ,
2015-03-16 10:03:05 -04:00
{
name : "invalid publicIPs localhost" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-08-11 20:18:21 -04:00
s . Spec . ExternalIPs = [ ] string { "127.0.0.1" }
2015-03-16 10:03:05 -04:00
} ,
numErrs : 1 ,
} ,
{
2016-01-04 12:49:39 -05:00
name : "invalid publicIPs unspecified" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-08-11 20:18:21 -04:00
s . Spec . ExternalIPs = [ ] string { "0.0.0.0" }
2015-03-16 10:03:05 -04:00
} ,
numErrs : 1 ,
} ,
2016-01-04 12:49:39 -05:00
{
name : "invalid publicIPs loopback" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . ExternalIPs = [ ] string { "127.0.0.1" }
} ,
numErrs : 1 ,
} ,
2015-03-16 10:03:05 -04:00
{
2015-08-11 20:18:21 -04:00
name : "invalid publicIPs host" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-08-11 20:18:21 -04:00
s . Spec . ExternalIPs = [ ] string { "myhost.mydomain" }
2015-03-16 10:03:05 -04:00
} ,
2015-08-11 20:18:21 -04:00
numErrs : 1 ,
2015-03-16 10:03:05 -04:00
} ,
2015-03-08 00:40:18 -05:00
{
2015-03-13 11:16:41 -04:00
name : "dup port name" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Name = "p"
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "p" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2014-10-31 02:03:52 -04:00
} ,
2015-03-13 11:16:41 -04:00
numErrs : 1 ,
2014-10-31 02:03:52 -04:00
} ,
2015-04-03 15:06:25 -04:00
{
2015-09-28 16:57:58 -04:00
name : "valid load balancer protocol UDP 1" ,
2015-04-03 15:06:25 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-22 17:49:26 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-04-03 15:06:25 -04:00
s . Spec . Ports [ 0 ] . Protocol = "UDP"
} ,
2015-09-28 16:57:58 -04:00
numErrs : 0 ,
2015-04-03 15:06:25 -04:00
} ,
{
2015-09-28 16:57:58 -04:00
name : "valid load balancer protocol UDP 2" ,
2015-04-03 15:06:25 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-22 17:49:26 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2016-02-03 17:54:32 -05:00
s . Spec . Ports [ 0 ] = api . ServicePort { Name : "q" , Port : 12345 , Protocol : "UDP" , TargetPort : intstr . FromInt ( 12345 ) }
2015-04-03 15:06:25 -04:00
} ,
2015-09-28 16:57:58 -04:00
numErrs : 0 ,
2015-04-03 15:06:25 -04:00
} ,
2016-02-03 17:54:32 -05:00
{
name : "invalid load balancer with mix protocol" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "UDP" , TargetPort : intstr . FromInt ( 12345 ) } )
} ,
numErrs : 1 ,
} ,
2014-10-31 02:03:52 -04:00
{
2015-03-08 00:40:18 -05:00
name : "valid 1" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-08 00:40:18 -05:00
// do nothing
2014-10-31 02:03:52 -04:00
} ,
numErrs : 0 ,
} ,
2014-10-23 16:14:13 -04:00
{
2015-03-08 00:40:18 -05:00
name : "valid 2" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-03-13 11:16:41 -04:00
s . Spec . Ports [ 0 ] . Protocol = "UDP"
2015-11-10 01:28:45 -05:00
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromInt ( 12345 )
2014-11-18 12:49:00 -05:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 0 ,
2014-11-18 12:49:00 -05:00
} ,
{
2015-03-08 00:40:18 -05:00
name : "valid 3" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-11-10 01:28:45 -05:00
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromString ( "http" )
2014-10-23 16:14:13 -04:00
} ,
2015-03-08 00:40:18 -05:00
numErrs : 0 ,
2014-10-23 16:14:13 -04:00
} ,
2015-03-16 17:36:30 -04:00
{
2015-05-23 16:41:11 -04:00
name : "valid cluster ip - none " ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-23 16:41:11 -04:00
s . Spec . ClusterIP = "None"
2015-03-16 17:36:30 -04:00
} ,
numErrs : 0 ,
} ,
{
2015-05-23 16:41:11 -04:00
name : "valid cluster ip - empty" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-23 16:41:11 -04:00
s . Spec . ClusterIP = ""
2015-11-10 01:28:45 -05:00
s . Spec . Ports [ 0 ] . TargetPort = intstr . FromString ( "http" )
2015-03-16 17:36:30 -04:00
} ,
numErrs : 0 ,
} ,
2015-04-03 15:06:25 -04:00
{
2015-05-20 11:59:34 -04:00
name : "valid type - cluster" ,
2015-05-22 17:49:26 -04:00
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
} ,
numErrs : 0 ,
} ,
{
2015-05-20 11:59:34 -04:00
name : "valid type - loadbalancer" ,
2015-05-22 17:49:26 -04:00
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
} ,
numErrs : 0 ,
} ,
{
name : "valid type loadbalancer 2 ports" ,
2015-04-03 15:06:25 -04:00
tweakSvc : func ( s * api . Service ) {
2015-05-22 17:49:26 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-04-03 15:06:25 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "valid external load balancer 2 ports" ,
tweakSvc : func ( s * api . Service ) {
2015-05-22 17:49:26 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-04-03 15:06:25 -04:00
} ,
numErrs : 0 ,
} ,
2015-05-22 17:54:19 -04:00
{
name : "duplicate nodeports" ,
tweakSvc : func ( s * api . Service ) {
2015-05-20 11:59:34 -04:00
s . Spec . Type = api . ServiceTypeNodePort
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 2 ) } )
2015-05-22 17:54:19 -04:00
} ,
2015-05-20 11:59:34 -04:00
numErrs : 1 ,
2015-05-22 17:54:19 -04:00
} ,
{
name : "duplicate nodeports (different protocols)" ,
tweakSvc : func ( s * api . Service ) {
2015-05-20 11:59:34 -04:00
s . Spec . Type = api . ServiceTypeNodePort
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "UDP" , NodePort : 1 , TargetPort : intstr . FromInt ( 2 ) } )
2015-05-22 17:54:19 -04:00
} ,
2015-05-20 11:59:34 -04:00
numErrs : 0 ,
} ,
2017-06-12 02:31:37 -04:00
{
name : "invalid duplicate ports (with same protocol)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 80 ) } )
} ,
numErrs : 1 ,
} ,
{
name : "valid duplicate ports (with different protocols)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 12345 , Protocol : "UDP" , TargetPort : intstr . FromInt ( 80 ) } )
} ,
numErrs : 0 ,
} ,
2017-06-10 00:55:47 -04:00
{
name : "invalid duplicate targetports (number with same protocol)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } )
} ,
numErrs : 1 ,
} ,
{
name : "invalid duplicate targetports (name with same protocol)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , TargetPort : intstr . FromString ( "http" ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "TCP" , TargetPort : intstr . FromString ( "http" ) } )
} ,
numErrs : 1 ,
} ,
{
name : "valid duplicate targetports (number with different protocols)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "UDP" , TargetPort : intstr . FromInt ( 8080 ) } )
} ,
numErrs : 0 ,
} ,
{
name : "valid duplicate targetports (name with different protocols)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , TargetPort : intstr . FromString ( "http" ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "r" , Port : 2 , Protocol : "UDP" , TargetPort : intstr . FromString ( "http" ) } )
} ,
numErrs : 0 ,
} ,
2015-05-20 11:59:34 -04:00
{
name : "valid type - cluster" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
} ,
numErrs : 0 ,
} ,
{
name : "valid type - nodeport" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
} ,
numErrs : 0 ,
} ,
{
name : "valid type - loadbalancer" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
} ,
numErrs : 0 ,
} ,
{
name : "valid type loadbalancer 2 ports" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "valid type loadbalancer with NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , NodePort : 12345 , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "valid type=NodePort service with NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , NodePort : 12345 , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "valid type=NodePort service without NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "valid cluster service without NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
} ,
{
name : "invalid cluster service with NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , NodePort : 12345 , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 1 ,
} ,
{
name : "invalid public service with duplicate NodePort" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "p1" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "p2" , Port : 2 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 2 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 1 ,
} ,
{
name : "valid type=LoadBalancer" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 12345 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-05-20 11:59:34 -04:00
} ,
numErrs : 0 ,
2015-05-22 17:54:19 -04:00
} ,
2015-07-09 01:02:10 -04:00
{
// For now we open firewalls, and its insecure if we open 10250, remove this
// when we have better protections in place.
name : "invalid port type=LoadBalancer" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
2015-11-10 01:28:45 -05:00
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "kubelet" , Port : 10250 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 12345 ) } )
2015-07-09 01:02:10 -04:00
} ,
numErrs : 1 ,
} ,
2016-02-29 20:56:39 -05:00
{
name : "valid LoadBalancer source range annotation" ,
tweakSvc : func ( s * api . Service ) {
2016-05-17 19:55:04 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2017-05-16 18:30:29 -04:00
s . Annotations [ api . AnnotationLoadBalancerSourceRangesKey ] = "1.2.3.4/8, 5.6.7.8/16"
2016-02-29 20:56:39 -05:00
} ,
numErrs : 0 ,
} ,
{
name : "empty LoadBalancer source range annotation" ,
tweakSvc : func ( s * api . Service ) {
2016-05-17 19:55:04 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2017-05-16 18:30:29 -04:00
s . Annotations [ api . AnnotationLoadBalancerSourceRangesKey ] = ""
2016-02-29 20:56:39 -05:00
} ,
numErrs : 0 ,
} ,
{
name : "invalid LoadBalancer source range annotation (hostname)" ,
tweakSvc : func ( s * api . Service ) {
2017-05-16 18:30:29 -04:00
s . Annotations [ api . AnnotationLoadBalancerSourceRangesKey ] = "foo.bar"
2016-02-29 20:56:39 -05:00
} ,
2016-05-17 19:55:04 -04:00
numErrs : 2 ,
2016-02-29 20:56:39 -05:00
} ,
{
name : "invalid LoadBalancer source range annotation (invalid CIDR)" ,
tweakSvc : func ( s * api . Service ) {
2016-05-17 19:55:04 -04:00
s . Spec . Type = api . ServiceTypeLoadBalancer
2017-05-16 18:30:29 -04:00
s . Annotations [ api . AnnotationLoadBalancerSourceRangesKey ] = "1.2.3.4/33"
2016-02-29 20:56:39 -05:00
} ,
numErrs : 1 ,
} ,
2016-05-17 19:55:04 -04:00
{
name : "invalid source range for non LoadBalancer type service" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . LoadBalancerSourceRanges = [ ] string { "1.2.3.4/8" , "5.6.7.8/16" }
} ,
numErrs : 1 ,
} ,
{
name : "valid LoadBalancer source range" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . LoadBalancerSourceRanges = [ ] string { "1.2.3.4/8" , "5.6.7.8/16" }
} ,
numErrs : 0 ,
} ,
{
name : "empty LoadBalancer source range" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . LoadBalancerSourceRanges = [ ] string { " " }
} ,
numErrs : 1 ,
} ,
{
name : "invalid LoadBalancer source range" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . LoadBalancerSourceRanges = [ ] string { "foo.bar" }
} ,
numErrs : 1 ,
} ,
2016-08-19 12:09:14 -04:00
{
name : "valid ExternalName" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeExternalName
s . Spec . ClusterIP = ""
s . Spec . ExternalName = "foo.bar.example.com"
} ,
numErrs : 0 ,
} ,
{
name : "invalid ExternalName clusterIP (valid IP)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeExternalName
s . Spec . ClusterIP = "1.2.3.4"
s . Spec . ExternalName = "foo.bar.example.com"
} ,
numErrs : 1 ,
} ,
{
name : "invalid ExternalName clusterIP (None)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeExternalName
s . Spec . ClusterIP = "None"
s . Spec . ExternalName = "foo.bar.example.com"
} ,
numErrs : 1 ,
} ,
{
name : "invalid ExternalName (not a DNS name)" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeExternalName
s . Spec . ClusterIP = ""
s . Spec . ExternalName = "-123"
} ,
numErrs : 1 ,
} ,
2016-09-22 10:07:47 -04:00
{
name : "LoadBalancer type cannot have None ClusterIP" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . ClusterIP = "None"
s . Spec . Type = api . ServiceTypeLoadBalancer
} ,
numErrs : 1 ,
} ,
2016-10-18 21:43:13 -04:00
{
2017-05-03 00:20:16 -04:00
name : "invalid node port with clusterIP None" ,
2016-10-18 21:43:13 -04:00
tweakSvc : func ( s * api . Service ) {
2017-05-03 00:20:16 -04:00
s . Spec . Type = api . ServiceTypeNodePort
s . Spec . Ports = append ( s . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
s . Spec . ClusterIP = "None"
} ,
numErrs : 1 ,
} ,
// ESIPP section begins.
{
name : "invalid externalTraffic field" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . ExternalTrafficPolicy = "invalid"
2016-08-18 17:50:09 -04:00
} ,
numErrs : 1 ,
} ,
2017-05-03 00:20:16 -04:00
{
name : "nagative healthCheckNodePort field" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
s . Spec . HealthCheckNodePort = - 1
} ,
numErrs : 1 ,
} ,
{
name : "nagative healthCheckNodePort field" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
s . Spec . HealthCheckNodePort = 31100
} ,
numErrs : 0 ,
} ,
// ESIPP section ends.
2017-08-01 12:09:37 -04:00
{
name : "invalid timeoutSeconds field" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . SessionAffinity = api . ServiceAffinityClientIP
s . Spec . SessionAffinityConfig = & api . SessionAffinityConfig {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( - 1 ) ,
} ,
}
} ,
numErrs : 1 ,
} ,
{
name : "sessionAffinityConfig can't be set when session affinity is None" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . SessionAffinity = api . ServiceAffinityNone
s . Spec . SessionAffinityConfig = & api . SessionAffinityConfig {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( 90 ) ,
} ,
}
} ,
numErrs : 1 ,
} ,
2014-07-10 15:45:01 -04:00
}
2014-09-10 12:41:31 -04:00
for _ , tc := range testCases {
2015-03-31 14:13:44 -04:00
svc := makeValidService ( )
tc . tweakSvc ( & svc )
2015-03-08 00:40:18 -05:00
errs := ValidateService ( & svc )
2014-09-10 12:41:31 -04:00
if len ( errs ) != tc . numErrs {
2015-11-03 19:08:20 -05:00
t . Errorf ( "Unexpected error list for case %q: %v" , tc . name , errs . ToAggregate ( ) )
2014-09-10 12:41:31 -04:00
}
2014-07-10 15:45:01 -04:00
}
}
2014-07-25 12:15:17 -04:00
2017-05-03 00:20:16 -04:00
func TestValidateServiceExternalTrafficFieldsCombination ( t * testing . T ) {
testCases := [ ] struct {
name string
tweakSvc func ( svc * api . Service ) // Given a basic valid service, each test case can customize it.
numErrs int
} {
{
name : "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
s . Spec . HealthCheckNodePort = 34567
} ,
numErrs : 0 ,
} ,
{
name : "valid nodePort service with externalTrafficPolicy set" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
} ,
numErrs : 0 ,
} ,
{
name : "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
} ,
numErrs : 0 ,
} ,
{
name : "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeLoadBalancer
2017-05-31 14:35:24 -04:00
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeCluster
2017-05-03 00:20:16 -04:00
s . Spec . HealthCheckNodePort = 34567
} ,
numErrs : 1 ,
} ,
{
name : "cannot set healthCheckNodePort field on nodePort service" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeNodePort
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
s . Spec . HealthCheckNodePort = 34567
} ,
numErrs : 1 ,
} ,
{
name : "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service" ,
tweakSvc : func ( s * api . Service ) {
s . Spec . Type = api . ServiceTypeClusterIP
s . Spec . ExternalTrafficPolicy = api . ServiceExternalTrafficPolicyTypeLocal
s . Spec . HealthCheckNodePort = 34567
} ,
numErrs : 2 ,
} ,
}
for _ , tc := range testCases {
svc := makeValidService ( )
tc . tweakSvc ( & svc )
errs := ValidateServiceExternalTrafficFieldsCombination ( & svc )
if len ( errs ) != tc . numErrs {
t . Errorf ( "Unexpected error list for case %q: %v" , tc . name , errs . ToAggregate ( ) )
}
}
}
2017-02-25 07:46:06 -05:00
func TestValidateReplicationControllerStatus ( t * testing . T ) {
tests := [ ] struct {
name string
replicas int32
fullyLabeledReplicas int32
readyReplicas int32
availableReplicas int32
observedGeneration int64
expectedErr bool
} {
{
name : "valid status" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 2 ,
availableReplicas : 1 ,
observedGeneration : 2 ,
expectedErr : false ,
} ,
{
name : "invalid replicas" ,
replicas : - 1 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 2 ,
availableReplicas : 1 ,
observedGeneration : 2 ,
expectedErr : true ,
} ,
{
name : "invalid fullyLabeledReplicas" ,
replicas : 3 ,
fullyLabeledReplicas : - 1 ,
readyReplicas : 2 ,
availableReplicas : 1 ,
observedGeneration : 2 ,
expectedErr : true ,
} ,
{
name : "invalid readyReplicas" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : - 1 ,
availableReplicas : 1 ,
observedGeneration : 2 ,
expectedErr : true ,
} ,
{
name : "invalid availableReplicas" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 3 ,
availableReplicas : - 1 ,
observedGeneration : 2 ,
expectedErr : true ,
} ,
{
name : "invalid observedGeneration" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 3 ,
availableReplicas : 3 ,
observedGeneration : - 1 ,
expectedErr : true ,
} ,
{
name : "fullyLabeledReplicas greater than replicas" ,
replicas : 3 ,
fullyLabeledReplicas : 4 ,
readyReplicas : 3 ,
availableReplicas : 3 ,
observedGeneration : 1 ,
expectedErr : true ,
} ,
{
name : "readyReplicas greater than replicas" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 4 ,
availableReplicas : 3 ,
observedGeneration : 1 ,
expectedErr : true ,
} ,
{
name : "availableReplicas greater than replicas" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 3 ,
availableReplicas : 4 ,
observedGeneration : 1 ,
expectedErr : true ,
} ,
{
name : "availableReplicas greater than readyReplicas" ,
replicas : 3 ,
fullyLabeledReplicas : 3 ,
readyReplicas : 2 ,
availableReplicas : 3 ,
observedGeneration : 1 ,
expectedErr : true ,
} ,
}
for _ , test := range tests {
status := api . ReplicationControllerStatus {
Replicas : test . replicas ,
FullyLabeledReplicas : test . fullyLabeledReplicas ,
ReadyReplicas : test . readyReplicas ,
AvailableReplicas : test . availableReplicas ,
ObservedGeneration : test . observedGeneration ,
}
if hasErr := len ( ValidateReplicationControllerStatus ( status , field . NewPath ( "status" ) ) ) > 0 ; hasErr != test . expectedErr {
t . Errorf ( "%s: expected error: %t, got error: %t" , test . name , test . expectedErr , hasErr )
}
}
}
2015-09-28 15:39:57 -04:00
func TestValidateReplicationControllerStatusUpdate ( t * testing . T ) {
validSelector := map [ string ] string { "a" : "b" }
validPodTemplate := api . PodTemplate {
Template : api . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-28 15:39:57 -04:00
Labels : validSelector ,
} ,
Spec : api . PodSpec {
RestartPolicy : api . RestartPolicyAlways ,
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-09-28 15:39:57 -04:00
} ,
} ,
}
type rcUpdateTest struct {
old api . ReplicationController
update api . ReplicationController
}
successCases := [ ] rcUpdateTest {
{
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-09-28 15:39:57 -04:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
Template : & validPodTemplate . Template ,
} ,
Status : api . ReplicationControllerStatus {
Replicas : 2 ,
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-09-28 15:39:57 -04:00
Spec : api . ReplicationControllerSpec {
Replicas : 3 ,
Selector : validSelector ,
Template : & validPodTemplate . Template ,
} ,
Status : api . ReplicationControllerStatus {
Replicas : 4 ,
} ,
} ,
} ,
}
for _ , successCase := range successCases {
successCase . old . ObjectMeta . ResourceVersion = "1"
successCase . update . ObjectMeta . ResourceVersion = "1"
2015-11-04 02:47:11 -05:00
if errs := ValidateReplicationControllerStatusUpdate ( & successCase . update , & successCase . old ) ; len ( errs ) != 0 {
2015-09-28 15:39:57 -04:00
t . Errorf ( "expected success: %v" , errs )
}
}
errorCases := map [ string ] rcUpdateTest {
"negative replicas" : {
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : metav1 . NamespaceDefault } ,
2015-09-28 15:39:57 -04:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
Template : & validPodTemplate . Template ,
} ,
Status : api . ReplicationControllerStatus {
Replicas : 3 ,
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-09-28 15:39:57 -04:00
Spec : api . ReplicationControllerSpec {
Replicas : 2 ,
Selector : validSelector ,
Template : & validPodTemplate . Template ,
} ,
Status : api . ReplicationControllerStatus {
Replicas : - 3 ,
} ,
} ,
} ,
}
for testName , errorCase := range errorCases {
2015-11-04 02:47:11 -05:00
if errs := ValidateReplicationControllerStatusUpdate ( & errorCase . update , & errorCase . old ) ; len ( errs ) == 0 {
2015-09-28 15:39:57 -04:00
t . Errorf ( "expected failure: %s" , testName )
}
}
}
2015-02-04 23:55:16 -05:00
func TestValidateReplicationControllerUpdate ( t * testing . T ) {
validSelector := map [ string ] string { "a" : "b" }
validPodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 23:55:16 -05:00
Labels : validSelector ,
} ,
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 23:55:16 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-02-04 23:55:16 -05:00
} ,
} ,
}
readWriteVolumePodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 23:55:16 -05:00
Labels : validSelector ,
} ,
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 23:55:16 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-08-07 21:52:23 -04:00
Volumes : [ ] api . Volume { { Name : "gcepd" , VolumeSource : api . VolumeSource { GCEPersistentDisk : & api . GCEPersistentDiskVolumeSource { PDName : "my-PD" , FSType : "ext4" , Partition : 1 , ReadOnly : false } } } } ,
2015-02-04 23:55:16 -05:00
} ,
} ,
}
invalidSelector := map [ string ] string { "NoUppercaseOrSpecialCharsLike=Equals" : "b" }
invalidPodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2015-02-04 23:55:16 -05:00
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 23:55:16 -05:00
DNSPolicy : api . DNSClusterFirst ,
} ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 23:55:16 -05:00
Labels : invalidSelector ,
} ,
} ,
}
type rcUpdateTest struct {
old api . ReplicationController
update api . ReplicationController
}
successCases := [ ] rcUpdateTest {
{
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 3 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
{
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 1 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & readWriteVolumePodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
}
for _ , successCase := range successCases {
2015-03-19 20:51:07 -04:00
successCase . old . ObjectMeta . ResourceVersion = "1"
successCase . update . ObjectMeta . ResourceVersion = "1"
2015-11-04 02:47:11 -05:00
if errs := ValidateReplicationControllerUpdate ( & successCase . update , & successCase . old ) ; len ( errs ) != 0 {
2015-02-04 23:55:16 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
errorCases := map [ string ] rcUpdateTest {
"more than one read/write" : {
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 2 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & readWriteVolumePodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
"invalid selector" : {
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 2 ,
Selector : invalidSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
"invalid pod" : {
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 2 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & invalidPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
"negative replicas" : {
old : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
update : api . ReplicationController {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : - 1 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
} ,
}
for testName , errorCase := range errorCases {
2015-11-04 02:47:11 -05:00
if errs := ValidateReplicationControllerUpdate ( & errorCase . update , & errorCase . old ) ; len ( errs ) == 0 {
2015-02-04 23:55:16 -05:00
t . Errorf ( "expected failure: %s" , testName )
}
}
}
2014-07-25 12:15:17 -04:00
func TestValidateReplicationController ( t * testing . T ) {
validSelector := map [ string ] string { "a" : "b" }
2014-08-29 21:20:27 -04:00
validPodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-06 21:08:46 -05:00
Labels : validSelector ,
2014-07-25 12:15:17 -04:00
} ,
2015-01-26 12:52:50 -05:00
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-26 12:52:50 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2015-01-26 12:52:50 -05:00
} ,
2014-07-25 12:15:17 -04:00
} ,
}
2015-02-04 23:55:16 -05:00
readWriteVolumePodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 23:55:16 -05:00
Labels : validSelector ,
} ,
2014-11-06 21:08:46 -05:00
Spec : api . PodSpec {
2015-08-07 21:52:23 -04:00
Volumes : [ ] api . Volume { { Name : "gcepd" , VolumeSource : api . VolumeSource { GCEPersistentDisk : & api . GCEPersistentDiskVolumeSource { PDName : "my-PD" , FSType : "ext4" , Partition : 1 , ReadOnly : false } } } } ,
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-02-04 18:44:46 -05:00
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "abc" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2014-08-05 13:58:43 -04:00
} ,
} ,
}
2014-10-23 16:14:13 -04:00
invalidSelector := map [ string ] string { "NoUppercaseOrSpecialCharsLike=Equals" : "b" }
invalidPodTemplate := api . PodTemplate {
2015-03-04 10:46:27 -05:00
Template : api . PodTemplateSpec {
2014-11-17 23:08:23 -05:00
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyAlways ,
2015-01-26 12:52:50 -05:00
DNSPolicy : api . DNSClusterFirst ,
2014-11-17 23:08:23 -05:00
} ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-06 21:08:46 -05:00
Labels : invalidSelector ,
2014-10-23 16:14:13 -04:00
} ,
} ,
}
2014-08-29 21:20:27 -04:00
successCases := [ ] api . ReplicationController {
2014-07-25 12:15:17 -04:00
{
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-07-25 12:15:17 -04:00
} ,
} ,
{
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc-123" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-07-25 12:15:17 -04:00
} ,
} ,
2015-02-04 23:55:16 -05:00
{
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc-123" , Namespace : metav1 . NamespaceDefault } ,
2015-02-04 23:55:16 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : 1 ,
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & readWriteVolumePodTemplate . Template ,
2015-02-04 23:55:16 -05:00
} ,
} ,
2014-07-25 12:15:17 -04:00
}
for _ , successCase := range successCases {
if errs := ValidateReplicationController ( & successCase ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
}
2014-08-29 21:20:27 -04:00
errorCases := map [ string ] api . ReplicationController {
2014-07-25 12:15:17 -04:00
"zero-length ID" : {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-09-29 17:18:18 -04:00
} ,
} ,
"missing-namespace" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc-123" } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-07-25 12:15:17 -04:00
} ,
} ,
"empty selector" : {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-07-25 12:15:17 -04:00
} ,
} ,
2014-08-21 20:02:39 -04:00
"selector_doesnt_match" : {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-08-21 20:02:39 -04:00
} ,
} ,
2014-07-25 12:15:17 -04:00
"invalid manifest" : {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2014-07-25 12:15:17 -04:00
} ,
} ,
2015-02-04 23:55:16 -05:00
"read-write persistent disk with > 1 pod" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
2015-02-04 23:55:16 -05:00
Replicas : 2 ,
2014-11-06 21:08:46 -05:00
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & readWriteVolumePodTemplate . Template ,
2014-08-05 13:58:43 -04:00
} ,
} ,
2014-08-04 15:02:51 -04:00
"negative_replicas" : {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : metav1 . NamespaceDefault } ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Replicas : - 1 ,
Selector : validSelector ,
2014-08-04 15:02:51 -04:00
} ,
} ,
2014-10-23 16:14:13 -04:00
"invalid_label" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-23 16:14:13 -04:00
Name : "abc-123" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2014-10-23 16:14:13 -04:00
Labels : map [ string ] string {
"NoUppercaseOrSpecialCharsLike=Equals" : "bar" ,
} ,
} ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
2015-03-04 10:46:27 -05:00
Template : & validPodTemplate . Template ,
2014-10-23 16:14:13 -04:00
} ,
} ,
"invalid_label 2" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-23 16:14:13 -04:00
Name : "abc-123" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2014-10-23 16:14:13 -04:00
Labels : map [ string ] string {
"NoUppercaseOrSpecialCharsLike=Equals" : "bar" ,
} ,
} ,
2014-11-06 21:08:46 -05:00
Spec : api . ReplicationControllerSpec {
2015-03-04 10:46:27 -05:00
Template : & invalidPodTemplate . Template ,
} ,
} ,
"invalid_annotation" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-04 10:46:27 -05:00
Name : "abc-123" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2015-03-04 10:46:27 -05:00
Annotations : map [ string ] string {
"NoUppercaseOrSpecialCharsLike=Equals" : "bar" ,
} ,
} ,
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
Template : & validPodTemplate . Template ,
2014-10-23 16:14:13 -04:00
} ,
} ,
2014-11-17 23:08:23 -05:00
"invalid restart policy 1" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 23:08:23 -05:00
Name : "abc-123" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2014-11-17 23:08:23 -05:00
} ,
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
Template : & api . PodTemplateSpec {
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyOnFailure ,
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2014-11-17 23:08:23 -05:00
} ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 23:08:23 -05:00
Labels : validSelector ,
} ,
} ,
} ,
} ,
"invalid restart policy 2" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 23:08:23 -05:00
Name : "abc-123" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2014-11-17 23:08:23 -05:00
} ,
Spec : api . ReplicationControllerSpec {
Selector : validSelector ,
Template : & api . PodTemplateSpec {
Spec : api . PodSpec {
2015-03-13 21:38:07 -04:00
RestartPolicy : api . RestartPolicyNever ,
DNSPolicy : api . DNSClusterFirst ,
2016-12-21 23:54:12 -05:00
Containers : [ ] api . Container { { Name : "ctr" , Image : "image" , ImagePullPolicy : "IfNotPresent" , TerminationMessagePolicy : "File" } } ,
2014-11-17 23:08:23 -05:00
} ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 23:08:23 -05:00
Labels : validSelector ,
} ,
} ,
} ,
} ,
2014-07-25 12:15:17 -04:00
}
for k , v := range errorCases {
2014-08-19 23:54:20 -04:00
errs := ValidateReplicationController ( & v )
if len ( errs ) == 0 {
2014-07-25 12:15:17 -04:00
t . Errorf ( "expected failure for %s" , k )
}
2014-08-19 23:54:20 -04:00
for i := range errs {
2015-11-03 19:08:20 -05:00
field := errs [ i ] . Field
2014-11-06 21:08:46 -05:00
if ! strings . HasPrefix ( field , "spec.template." ) &&
2015-08-12 11:26:23 -04:00
field != "metadata.name" &&
2015-01-27 18:55:54 -05:00
field != "metadata.namespace" &&
2014-11-06 21:08:46 -05:00
field != "spec.selector" &&
field != "spec.template" &&
2014-08-05 13:58:43 -04:00
field != "GCEPersistentDisk.ReadOnly" &&
2014-11-06 21:08:46 -05:00
field != "spec.replicas" &&
2014-11-20 00:55:45 -05:00
field != "spec.template.labels" &&
2015-01-27 18:55:54 -05:00
field != "metadata.annotations" &&
2015-09-28 15:39:57 -04:00
field != "metadata.labels" &&
field != "status.replicas" {
2014-08-19 23:54:20 -04:00
t . Errorf ( "%s: missing prefix for: %v" , k , errs [ i ] )
}
}
2014-07-25 12:15:17 -04:00
}
}
2014-10-08 15:56:02 -04:00
2015-04-22 13:55:05 -04:00
func TestValidateNode ( t * testing . T ) {
2014-11-12 12:38:15 -05:00
validSelector := map [ string ] string { "a" : "b" }
invalidSelector := map [ string ] string { "NoUppercaseOrSpecialCharsLike=Equals" : "b" }
2014-12-07 22:44:27 -05:00
successCases := [ ] api . Node {
2014-11-12 12:38:15 -05:00
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-19 17:39:10 -05:00
Name : "abc" ,
Labels : validSelector ,
} ,
Status : api . NodeStatus {
2015-02-13 14:07:23 -05:00
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2015-02-13 14:07:23 -05:00
} ,
2015-03-24 13:24:07 -04:00
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
api . ResourceName ( "my.org/gpu" ) : resource . MustParse ( "10" ) ,
} ,
} ,
2015-03-25 09:44:40 -04:00
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
2014-11-12 12:38:15 -05:00
} ,
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-01-27 18:56:38 -05:00
Name : "abc" ,
} ,
2014-11-19 17:39:10 -05:00
Status : api . NodeStatus {
2015-02-13 14:07:23 -05:00
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2015-02-13 14:07:23 -05:00
} ,
2015-03-24 13:24:07 -04:00
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
2015-03-25 09:44:40 -04:00
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
2014-11-12 12:38:15 -05:00
} ,
2016-03-30 23:42:57 -04:00
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node1" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2016-03-30 23:42:57 -04:00
} ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a valid taint to a node
Taints : [ ] api . Taint { { Key : "GPU" , Value : "true" , Effect : "NoSchedule" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
2016-02-25 04:30:31 -05:00
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "abc" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"podSignature" : {
"podController" : {
"apiVersion" : "v1" ,
"kind" : "ReplicationController" ,
"name" : "foo" ,
"uid" : "abcdef123456" ,
"controller" : true
}
} ,
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2016-02-25 04:30:31 -05:00
} ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
} ,
2014-11-12 12:38:15 -05:00
}
for _ , successCase := range successCases {
2015-04-22 13:55:05 -04:00
if errs := ValidateNode ( & successCase ) ; len ( errs ) != 0 {
2014-11-12 12:38:15 -05:00
t . Errorf ( "expected success: %v" , errs )
}
}
2014-12-07 22:44:27 -05:00
errorCases := map [ string ] api . Node {
2014-11-12 12:38:15 -05:00
"zero-length Name" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-19 17:39:10 -05:00
Name : "" ,
Labels : validSelector ,
} ,
Status : api . NodeStatus {
2015-02-13 14:07:23 -05:00
Addresses : [ ] api . NodeAddress { } ,
2015-03-24 13:24:07 -04:00
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
} ,
2015-03-25 09:44:40 -04:00
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
2014-11-12 12:38:15 -05:00
} ,
"invalid-labels" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-19 17:39:10 -05:00
Name : "abc-123" ,
Labels : invalidSelector ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2015-03-24 13:24:07 -04:00
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
} ,
2015-03-25 09:44:40 -04:00
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
2015-03-24 13:24:07 -04:00
} ,
"missing-external-id" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-24 13:24:07 -04:00
Name : "abc-123" ,
Labels : validSelector ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2015-03-24 13:24:07 -04:00
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
} ,
} ,
} ,
2016-03-30 23:42:57 -04:00
"missing-taint-key" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node1" ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a taint with an empty key to a node
Taints : [ ] api . Taint { { Key : "" , Value : "special-user-1" , Effect : "NoSchedule" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"bad-taint-key" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node1" ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a taint with an invalid key to a node
Taints : [ ] api . Taint { { Key : "NoUppercaseOrSpecialCharsLike=Equals" , Value : "special-user-1" , Effect : "NoSchedule" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"bad-taint-value" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node2" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2016-03-30 23:42:57 -04:00
} ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a taint with a bad value to a node
Taints : [ ] api . Taint { { Key : "dedicated" , Value : "some\\bad\\value" , Effect : "NoSchedule" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
"missing-taint-effect" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node3" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2016-03-30 23:42:57 -04:00
} ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a taint with an empty effect to a node
Taints : [ ] api . Taint { { Key : "dedicated" , Value : "special-user-3" , Effect : "" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
2017-01-04 06:45:50 -05:00
"invalid-taint-effect" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-30 23:42:57 -04:00
Name : "dedicated-node3" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "something" } ,
2016-03-30 23:42:57 -04:00
} ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add a taint with NoExecute effect to a node
Taints : [ ] api . Taint { { Key : "dedicated" , Value : "special-user-3" , Effect : "NoScheduleNoAdmit" } } ,
2016-03-30 23:42:57 -04:00
} ,
} ,
2016-08-12 04:46:40 -04:00
"duplicated-taints-with-same-key-effect" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-12 04:46:40 -04:00
Name : "dedicated-node1" ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
2017-02-07 09:08:45 -05:00
// Add two taints to the node with the same key and effect; should be rejected.
Taints : [ ] api . Taint {
{ Key : "dedicated" , Value : "special-user-1" , Effect : "NoSchedule" } ,
{ Key : "dedicated" , Value : "special-user-2" , Effect : "NoSchedule" } ,
} ,
2016-08-12 04:46:40 -04:00
} ,
} ,
2016-02-25 04:30:31 -05:00
"missing-podSignature" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "abc-123" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress { } ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
} ,
"invalid-podController" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "abc-123" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"podSignature" : {
"podController" : {
"apiVersion" : "v1" ,
"kind" : "ReplicationController" ,
"name" : "foo" ,
"uid" : "abcdef123456" ,
"controller" : false
}
} ,
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress { } ,
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "0" ) ,
} ,
} ,
Spec : api . NodeSpec {
ExternalID : "external" ,
} ,
} ,
2014-11-12 12:38:15 -05:00
}
for k , v := range errorCases {
2015-04-22 13:55:05 -04:00
errs := ValidateNode ( & v )
2014-11-12 12:38:15 -05:00
if len ( errs ) == 0 {
t . Errorf ( "expected failure for %s" , k )
}
for i := range errs {
2015-11-03 19:08:20 -05:00
field := errs [ i ] . Field
2015-03-24 13:24:07 -04:00
expectedFields := map [ string ] bool {
2016-02-25 04:30:31 -05:00
"metadata.name" : true ,
"metadata.labels" : true ,
"metadata.annotations" : true ,
"metadata.namespace" : true ,
"spec.externalID" : true ,
2017-02-07 09:08:45 -05:00
"spec.taints[0].key" : true ,
"spec.taints[0].value" : true ,
"spec.taints[0].effect" : true ,
2016-02-25 04:30:31 -05:00
"metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature" : true ,
"metadata.annotations.scheduler.alpha.kubernetes.io/preferAvoidPods[0].PodSignature.PodController.Controller" : true ,
2015-03-24 13:24:07 -04:00
}
2016-03-30 23:42:57 -04:00
if val , ok := expectedFields [ field ] ; ok {
if ! val {
t . Errorf ( "%s: missing prefix for: %v" , k , errs [ i ] )
}
2014-11-12 12:38:15 -05:00
}
}
}
}
2014-11-17 13:22:27 -05:00
2015-04-22 13:55:05 -04:00
func TestValidateNodeUpdate ( t * testing . T ) {
2014-11-17 13:22:27 -05:00
tests := [ ] struct {
2015-04-22 13:55:05 -04:00
oldNode api . Node
node api . Node
valid bool
2014-11-17 13:22:27 -05:00
} {
2014-12-07 22:44:27 -05:00
{ api . Node { } , api . Node { } , true } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" } } ,
2014-12-07 22:44:27 -05:00
api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "bar" } ,
} , false } ,
2014-12-07 22:44:27 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
Labels : map [ string ] string { "foo" : "bar" } ,
} ,
2014-12-07 22:44:27 -05:00
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
2014-12-07 22:44:27 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
} ,
2014-12-07 22:44:27 -05:00
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
2014-12-07 22:44:27 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
Labels : map [ string ] string { "bar" : "foo" } ,
} ,
2014-12-07 22:44:27 -05:00
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-17 13:22:27 -05:00
Name : "foo" ,
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
2016-01-30 22:49:31 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-30 22:49:31 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
PodCIDR : "" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-30 22:49:31 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
PodCIDR : "192.168.0.0/16" ,
} ,
} , true } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-30 22:49:31 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
PodCIDR : "192.123.0.0/16" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-30 22:49:31 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
PodCIDR : "192.168.0.0/16" ,
} ,
} , false } ,
2014-12-12 00:39:56 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-12 00:39:56 -05:00
Name : "foo" ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2014-12-12 00:39:56 -05:00
Capacity : api . ResourceList {
2015-01-06 20:20:01 -05:00
api . ResourceCPU : resource . MustParse ( "10000" ) ,
api . ResourceMemory : resource . MustParse ( "100" ) ,
2014-12-12 00:39:56 -05:00
} ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-12 00:39:56 -05:00
Name : "foo" ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2014-12-12 00:39:56 -05:00
Capacity : api . ResourceList {
2015-01-06 20:20:01 -05:00
api . ResourceCPU : resource . MustParse ( "100" ) ,
api . ResourceMemory : resource . MustParse ( "10000" ) ,
2014-12-12 00:39:56 -05:00
} ,
} ,
} , true } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-12 00:39:56 -05:00
Name : "foo" ,
Labels : map [ string ] string { "bar" : "foo" } ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2014-12-12 00:39:56 -05:00
Capacity : api . ResourceList {
2015-01-06 20:20:01 -05:00
api . ResourceCPU : resource . MustParse ( "10000" ) ,
api . ResourceMemory : resource . MustParse ( "100" ) ,
2014-12-12 00:39:56 -05:00
} ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-12 00:39:56 -05:00
Name : "foo" ,
Labels : map [ string ] string { "bar" : "fooobaz" } ,
} ,
2015-03-25 09:44:40 -04:00
Status : api . NodeStatus {
2014-12-12 00:39:56 -05:00
Capacity : api . ResourceList {
2015-01-06 20:20:01 -05:00
api . ResourceCPU : resource . MustParse ( "100" ) ,
api . ResourceMemory : resource . MustParse ( "10000" ) ,
2014-12-12 00:39:56 -05:00
} ,
} ,
} , true } ,
2014-12-22 14:04:57 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-22 14:04:57 -05:00
Name : "foo" ,
Labels : map [ string ] string { "bar" : "foo" } ,
} ,
Status : api . NodeStatus {
2015-02-13 14:07:23 -05:00
Addresses : [ ] api . NodeAddress {
2017-04-23 22:21:29 -04:00
{ Type : api . NodeExternalIP , Address : "1.2.3.4" } ,
2015-02-13 14:07:23 -05:00
} ,
2014-12-22 14:04:57 -05:00
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-12-22 14:04:57 -05:00
Name : "foo" ,
Labels : map [ string ] string { "bar" : "fooobaz" } ,
} ,
} , true } ,
2015-02-04 17:21:34 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 17:21:34 -05:00
Name : "foo" ,
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-04 17:21:34 -05:00
Name : "foo" ,
Labels : map [ string ] string { "Foo" : "baz" } ,
} ,
2015-02-27 10:08:02 -05:00
} , true } ,
2015-02-17 15:03:14 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-17 15:03:14 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
Unschedulable : false ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-02-17 15:03:14 -05:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
Unschedulable : true ,
} ,
} , true } ,
2015-04-22 13:55:05 -04:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-22 13:55:05 -04:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
Unschedulable : false ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-22 13:55:05 -04:00
Name : "foo" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
{ Type : api . NodeExternalIP , Address : "1.1.1.1" } ,
{ Type : api . NodeExternalIP , Address : "1.1.1.1" } ,
} ,
} ,
} , false } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-22 13:55:05 -04:00
Name : "foo" ,
} ,
Spec : api . NodeSpec {
Unschedulable : false ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-22 13:55:05 -04:00
Name : "foo" ,
} ,
Status : api . NodeStatus {
Addresses : [ ] api . NodeAddress {
{ Type : api . NodeExternalIP , Address : "1.1.1.1" } ,
{ Type : api . NodeInternalIP , Address : "10.1.1.1" } ,
} ,
} ,
} , true } ,
2016-02-25 04:30:31 -05:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"podSignature" : {
"podController" : {
"apiVersion" : "v1" ,
"kind" : "ReplicationController" ,
"name" : "foo" ,
"uid" : "abcdef123456" ,
"controller" : true
}
} ,
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
Spec : api . NodeSpec {
Unschedulable : false ,
} ,
} , true } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
} , false } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-25 04:30:31 -05:00
Name : "foo" ,
Annotations : map [ string ] string {
api . PreferAvoidPodsAnnotationKey : `
{
"preferAvoidPods" : [
{
"podSignature" : {
"podController" : {
"apiVersion" : "v1" ,
"kind" : "ReplicationController" ,
"name" : "foo" ,
"uid" : "abcdef123456" ,
"controller" : false
}
} ,
"reason" : "some reason" ,
"message" : "some message"
}
]
} ` ,
} ,
} ,
} , false } ,
2016-09-26 11:11:31 -04:00
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "valid-opaque-int-resources" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "valid-opaque-int-resources" ,
} ,
Status : api . NodeStatus {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "5" ) ,
helper . OpaqueIntResourceName ( "B" ) : resource . MustParse ( "10" ) ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} , true } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "invalid-fractional-opaque-int-capacity" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "invalid-fractional-opaque-int-capacity" ,
} ,
Status : api . NodeStatus {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "500m" ) ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} , false } ,
{ api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "invalid-fractional-opaque-int-allocatable" ,
} ,
} , api . Node {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-09-26 11:11:31 -04:00
Name : "invalid-fractional-opaque-int-allocatable" ,
} ,
Status : api . NodeStatus {
Capacity : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "5" ) ,
2016-09-26 11:11:31 -04:00
} ,
Allocatable : api . ResourceList {
api . ResourceName ( api . ResourceCPU ) : resource . MustParse ( "10" ) ,
api . ResourceName ( api . ResourceMemory ) : resource . MustParse ( "10G" ) ,
2017-04-10 13:49:54 -04:00
helper . OpaqueIntResourceName ( "A" ) : resource . MustParse ( "4.5" ) ,
2016-09-26 11:11:31 -04:00
} ,
} ,
} , false } ,
2014-11-17 13:22:27 -05:00
}
2015-01-27 18:55:54 -05:00
for i , test := range tests {
2015-04-22 13:55:05 -04:00
test . oldNode . ObjectMeta . ResourceVersion = "1"
test . node . ObjectMeta . ResourceVersion = "1"
2015-11-04 02:47:11 -05:00
errs := ValidateNodeUpdate ( & test . node , & test . oldNode )
2014-11-17 13:22:27 -05:00
if test . valid && len ( errs ) > 0 {
2015-01-27 18:55:54 -05:00
t . Errorf ( "%d: Unexpected error: %v" , i , errs )
2015-04-22 13:55:05 -04:00
t . Logf ( "%#v vs %#v" , test . oldNode . ObjectMeta , test . node . ObjectMeta )
2014-11-17 13:22:27 -05:00
}
if ! test . valid && len ( errs ) == 0 {
2015-01-27 18:55:54 -05:00
t . Errorf ( "%d: Unexpected non-error" , i )
}
}
}
func TestValidateServiceUpdate ( t * testing . T ) {
2015-03-31 14:13:44 -04:00
testCases := [ ] struct {
name string
tweakSvc func ( oldSvc , newSvc * api . Service ) // given basic valid services, each test case can customize them
numErrs int
2015-01-27 18:55:54 -05:00
} {
2015-03-31 14:13:44 -04:00
{
name : "no change" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
// do nothing
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 0 ,
} ,
{
name : "change name" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Name += "2"
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 1 ,
} ,
{
name : "change namespace" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Namespace += "2"
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 1 ,
} ,
{
name : "change label valid" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Labels [ "key" ] = "other-value"
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 0 ,
} ,
{
name : "add label" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Labels [ "key2" ] = "value2"
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 0 ,
} ,
{
2015-05-23 16:41:11 -04:00
name : "change cluster IP" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
2015-05-23 16:41:11 -04:00
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "8.6.7.5"
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 1 ,
} ,
{
2015-05-23 16:41:11 -04:00
name : "remove cluster IP" ,
2015-03-31 14:13:44 -04:00
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
2015-05-23 16:41:11 -04:00
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = ""
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 1 ,
} ,
{
name : "change affinity" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . SessionAffinity = "ClientIP"
2017-08-01 12:09:37 -04:00
newSvc . Spec . SessionAffinityConfig = & api . SessionAffinityConfig {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( 90 ) ,
} ,
}
2015-01-27 18:55:54 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 0 ,
} ,
{
name : "remove affinity" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . SessionAffinity = ""
2015-02-04 17:21:34 -05:00
} ,
2015-03-31 14:13:44 -04:00
numErrs : 1 ,
} ,
2015-05-22 17:49:26 -04:00
{
name : "change type" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
} ,
numErrs : 0 ,
} ,
{
name : "remove type" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . Type = ""
} ,
numErrs : 1 ,
} ,
2015-05-20 11:59:34 -04:00
{
name : "change type -> nodeport" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . Type = api . ServiceTypeNodePort
} ,
numErrs : 0 ,
} ,
2016-09-22 20:41:25 -04:00
{
name : "add loadBalancerSourceRanges" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . LoadBalancerSourceRanges = [ ] string { "10.0.0.0/8" }
} ,
2016-11-30 17:04:22 -05:00
numErrs : 0 ,
2016-09-22 20:41:25 -04:00
} ,
{
name : "update loadBalancerSourceRanges" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . LoadBalancerSourceRanges = [ ] string { "10.0.0.0/8" }
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
2017-06-16 01:15:08 -04:00
newSvc . Spec . LoadBalancerSourceRanges = [ ] string { "10.100.0.0/16" }
2016-09-22 20:41:25 -04:00
} ,
2016-11-30 17:04:22 -05:00
numErrs : 0 ,
2016-09-22 20:41:25 -04:00
} ,
2016-09-22 10:07:47 -04:00
{
name : "LoadBalancer type cannot have None ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
newSvc . Spec . ClusterIP = "None"
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
} ,
numErrs : 1 ,
} ,
2016-10-13 04:17:44 -04:00
{
name : "`None` ClusterIP cannot be changed" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . ClusterIP = "None"
newSvc . Spec . ClusterIP = "1.2.3.4"
} ,
numErrs : 1 ,
} ,
{
name : "`None` ClusterIP cannot be removed" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . ClusterIP = "None"
newSvc . Spec . ClusterIP = ""
} ,
numErrs : 1 ,
} ,
{
name : "Service with ClusterIP type cannot change its set ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with ClusterIP type can change its empty ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with ClusterIP type cannot change its set ClusterIP when changing type to NodePort" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with ClusterIP type can change its empty ClusterIP when changing type to NodePort" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with ClusterIP type cannot change its ClusterIP when changing type to LoadBalancer" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with ClusterIP type can change its empty ClusterIP when changing type to LoadBalancer" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeClusterIP
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with NodePort type cannot change its set ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with NodePort type can change its empty ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with NodePort type cannot change its set ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with NodePort type can change its empty ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with NodePort type cannot change its set ClusterIP when changing type to LoadBalancer" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with NodePort type can change its empty ClusterIP when changing type to LoadBalancer" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with LoadBalancer type cannot change its set ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with LoadBalancer type can change its empty ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeLoadBalancer
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with LoadBalancer type cannot change its set ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with LoadBalancer type can change its empty ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with LoadBalancer type cannot change its set ClusterIP when changing type to NodePort" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 1 ,
} ,
{
name : "Service with LoadBalancer type can change its empty ClusterIP when changing type to NodePort" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeLoadBalancer
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with ExternalName type can change its empty ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeExternalName
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
{
name : "Service with ExternalName type can change its set ClusterIP when changing type to ClusterIP" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeExternalName
newSvc . Spec . Type = api . ServiceTypeClusterIP
oldSvc . Spec . ClusterIP = "1.2.3.4"
newSvc . Spec . ClusterIP = "1.2.3.5"
} ,
numErrs : 0 ,
} ,
2016-08-18 17:50:09 -04:00
{
name : "invalid node port with clusterIP None" ,
tweakSvc : func ( oldSvc , newSvc * api . Service ) {
oldSvc . Spec . Type = api . ServiceTypeNodePort
newSvc . Spec . Type = api . ServiceTypeNodePort
oldSvc . Spec . Ports = append ( oldSvc . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
newSvc . Spec . Ports = append ( newSvc . Spec . Ports , api . ServicePort { Name : "q" , Port : 1 , Protocol : "TCP" , NodePort : 1 , TargetPort : intstr . FromInt ( 1 ) } )
oldSvc . Spec . ClusterIP = ""
newSvc . Spec . ClusterIP = "None"
} ,
numErrs : 1 ,
} ,
2015-01-27 18:55:54 -05:00
}
2015-03-31 14:13:44 -04:00
for _ , tc := range testCases {
oldSvc := makeValidService ( )
newSvc := makeValidService ( )
tc . tweakSvc ( & oldSvc , & newSvc )
2015-11-04 02:47:11 -05:00
errs := ValidateServiceUpdate ( & newSvc , & oldSvc )
2015-03-31 14:13:44 -04:00
if len ( errs ) != tc . numErrs {
2015-11-03 19:08:20 -05:00
t . Errorf ( "Unexpected error list for case %q: %v" , tc . name , errs . ToAggregate ( ) )
2014-11-17 13:22:27 -05:00
}
}
}
2015-01-16 19:34:47 -05:00
func TestValidateResourceNames ( t * testing . T ) {
table := [ ] struct {
input string
success bool
2015-12-16 00:28:42 -05:00
expect string
2015-01-16 19:34:47 -05:00
} {
2015-12-16 00:28:42 -05:00
{ "memory" , true , "" } ,
{ "cpu" , true , "" } ,
2016-10-10 20:49:05 -04:00
{ "storage" , true , "" } ,
{ "requests.cpu" , true , "" } ,
{ "requests.memory" , true , "" } ,
{ "requests.storage" , true , "" } ,
{ "limits.cpu" , true , "" } ,
{ "limits.memory" , true , "" } ,
2015-12-16 00:28:42 -05:00
{ "network" , false , "" } ,
{ "disk" , false , "" } ,
{ "" , false , "" } ,
{ "." , false , "" } ,
{ ".." , false , "" } ,
{ "my.favorite.app.co/12345" , true , "" } ,
{ "my.favorite.app.co/_12345" , false , "" } ,
{ "my.favorite.app.co/12345_" , false , "" } ,
{ "kubernetes.io/.." , false , "" } ,
{ "kubernetes.io/" + strings . Repeat ( "a" , 63 ) , true , "" } ,
{ "kubernetes.io/" + strings . Repeat ( "a" , 64 ) , false , "" } ,
{ "kubernetes.io//" , false , "" } ,
{ "kubernetes.io" , false , "" } ,
{ "kubernetes.io/will/not/work/" , false , "" } ,
2015-01-16 19:34:47 -05:00
}
2015-02-04 19:36:27 -05:00
for k , item := range table {
2015-11-06 18:30:52 -05:00
err := validateResourceName ( item . input , field . NewPath ( "field" ) )
2015-01-16 19:34:47 -05:00
if len ( err ) != 0 && item . success {
t . Errorf ( "expected no failure for input %q" , item . input )
} else if len ( err ) == 0 && ! item . success {
t . Errorf ( "expected failure for input %q" , item . input )
2015-02-04 19:36:27 -05:00
for i := range err {
2015-11-03 19:08:20 -05:00
detail := err [ i ] . Detail
2015-12-16 00:28:42 -05:00
if detail != "" && ! strings . Contains ( detail , item . expect ) {
t . Errorf ( "%d: expected error detail either empty or %s, got %s" , k , item . expect , detail )
2015-02-04 19:36:27 -05:00
}
}
2015-01-16 19:34:47 -05:00
}
}
}
2015-01-22 16:52:40 -05:00
2015-09-08 14:49:54 -04:00
func getResourceList ( cpu , memory string ) api . ResourceList {
res := api . ResourceList { }
if cpu != "" {
res [ api . ResourceCPU ] = resource . MustParse ( cpu )
2015-06-16 16:16:34 -04:00
}
2015-09-08 14:49:54 -04:00
if memory != "" {
res [ api . ResourceMemory ] = resource . MustParse ( memory )
2015-08-28 12:26:36 -04:00
}
2015-09-08 14:49:54 -04:00
return res
}
2015-08-28 12:26:36 -04:00
2016-03-09 18:11:26 -05:00
func getStorageResourceList ( storage string ) api . ResourceList {
res := api . ResourceList { }
if storage != "" {
res [ api . ResourceStorage ] = resource . MustParse ( storage )
}
return res
}
2017-08-16 04:55:17 -04:00
func getLocalStorageResourceList ( ephemeralStorage string ) api . ResourceList {
res := api . ResourceList { }
if ephemeralStorage != "" {
res [ api . ResourceEphemeralStorage ] = resource . MustParse ( ephemeralStorage )
}
return res
}
func TestValidateLimitRangeForLocalStorage ( t * testing . T ) {
testCases := [ ] struct {
name string
spec api . LimitRangeSpec
} {
{
name : "all-fields-valid" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getLocalStorageResourceList ( "10000Mi" ) ,
Min : getLocalStorageResourceList ( "100Mi" ) ,
MaxLimitRequestRatio : getLocalStorageResourceList ( "" ) ,
} ,
{
Type : api . LimitTypeContainer ,
Max : getLocalStorageResourceList ( "10000Mi" ) ,
Min : getLocalStorageResourceList ( "100Mi" ) ,
Default : getLocalStorageResourceList ( "500Mi" ) ,
DefaultRequest : getLocalStorageResourceList ( "200Mi" ) ,
MaxLimitRequestRatio : getLocalStorageResourceList ( "" ) ,
} ,
} ,
} ,
} ,
}
// Enable alpha feature LocalStorageCapacityIsolation
err := utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=true" )
if err != nil {
t . Errorf ( "Failed to enable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , testCase := range testCases {
limitRange := & api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : testCase . name , Namespace : "foo" } , Spec : testCase . spec }
if errs := ValidateLimitRange ( limitRange ) ; len ( errs ) != 0 {
t . Errorf ( "Case %v, unexpected error: %v" , testCase . name , errs )
}
}
// Disable alpha feature LocalStorageCapacityIsolation
err = utilfeature . DefaultFeatureGate . Set ( "LocalStorageCapacityIsolation=false" )
if err != nil {
t . Errorf ( "Failed to disable feature gate for LocalStorageCapacityIsolation: %v" , err )
return
}
for _ , testCase := range testCases {
limitRange := & api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : testCase . name , Namespace : "foo" } , Spec : testCase . spec }
if errs := ValidateLimitRange ( limitRange ) ; len ( errs ) == 0 {
t . Errorf ( "Case %v, expected feature gate unable error but actually no error" , testCase . name )
}
}
}
2015-09-08 14:49:54 -04:00
func TestValidateLimitRange ( t * testing . T ) {
successCases := [ ] struct {
name string
spec api . LimitRangeSpec
} {
{
name : "all-fields-valid" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getResourceList ( "100m" , "10000Mi" ) ,
Min : getResourceList ( "5m" , "100Mi" ) ,
2015-09-10 16:39:59 -04:00
MaxLimitRequestRatio : getResourceList ( "10" , "" ) ,
} ,
{
Type : api . LimitTypeContainer ,
Max : getResourceList ( "100m" , "10000Mi" ) ,
Min : getResourceList ( "5m" , "100Mi" ) ,
2015-09-08 14:49:54 -04:00
Default : getResourceList ( "50m" , "500Mi" ) ,
DefaultRequest : getResourceList ( "10m" , "200Mi" ) ,
MaxLimitRequestRatio : getResourceList ( "10" , "" ) ,
} ,
2016-08-15 10:19:15 -04:00
{
Type : api . LimitTypePersistentVolumeClaim ,
Max : getStorageResourceList ( "10Gi" ) ,
Min : getStorageResourceList ( "5Gi" ) ,
} ,
2015-08-28 12:26:36 -04:00
} ,
} ,
} ,
2016-10-24 11:01:18 -04:00
{
name : "pvc-min-only" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePersistentVolumeClaim ,
Min : getStorageResourceList ( "5Gi" ) ,
} ,
} ,
} ,
} ,
{
name : "pvc-max-only" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePersistentVolumeClaim ,
Max : getStorageResourceList ( "10Gi" ) ,
} ,
} ,
} ,
} ,
2015-01-22 16:52:40 -05:00
{
2015-09-08 14:49:54 -04:00
name : "all-fields-valid-big-numbers" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
2015-09-10 16:39:59 -04:00
Type : api . LimitTypeContainer ,
2015-09-08 14:49:54 -04:00
Max : getResourceList ( "100m" , "10000T" ) ,
Min : getResourceList ( "5m" , "100Mi" ) ,
Default : getResourceList ( "50m" , "500Mi" ) ,
DefaultRequest : getResourceList ( "10m" , "200Mi" ) ,
MaxLimitRequestRatio : getResourceList ( "10" , "" ) ,
} ,
} ,
2015-01-22 16:52:40 -05:00
} ,
} ,
2016-03-09 18:11:26 -05:00
{
name : "thirdparty-fields-all-valid-standard-container-resources" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : "thirdparty.com/foo" ,
Max : getResourceList ( "100m" , "10000T" ) ,
Min : getResourceList ( "5m" , "100Mi" ) ,
Default : getResourceList ( "50m" , "500Mi" ) ,
DefaultRequest : getResourceList ( "10m" , "200Mi" ) ,
MaxLimitRequestRatio : getResourceList ( "10" , "" ) ,
} ,
} ,
} ,
} ,
{
name : "thirdparty-fields-all-valid-storage-resources" ,
spec : api . LimitRangeSpec {
Limits : [ ] api . LimitRangeItem {
{
Type : "thirdparty.com/foo" ,
Max : getStorageResourceList ( "10000T" ) ,
Min : getStorageResourceList ( "100Mi" ) ,
Default : getStorageResourceList ( "500Mi" ) ,
DefaultRequest : getStorageResourceList ( "200Mi" ) ,
MaxLimitRequestRatio : getStorageResourceList ( "" ) ,
} ,
} ,
} ,
} ,
2015-01-22 16:52:40 -05:00
}
2015-01-23 12:38:30 -05:00
2015-01-22 16:52:40 -05:00
for _ , successCase := range successCases {
2017-01-16 22:38:19 -05:00
limitRange := & api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : successCase . name , Namespace : "foo" } , Spec : successCase . spec }
2015-09-08 14:49:54 -04:00
if errs := ValidateLimitRange ( limitRange ) ; len ( errs ) != 0 {
t . Errorf ( "Case %v, unexpected error: %v" , successCase . name , errs )
2015-01-22 16:52:40 -05:00
}
}
2015-02-04 19:36:27 -05:00
errorCases := map [ string ] struct {
R api . LimitRange
D string
} {
2015-09-08 14:49:54 -04:00
"zero-length-name" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : "foo" } , Spec : api . LimitRangeSpec { } } ,
2015-08-12 11:26:23 -04:00
"name or generateName is required" ,
2015-01-22 16:52:40 -05:00
} ,
"zero-length-namespace" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "" } , Spec : api . LimitRangeSpec { } } ,
2015-02-04 19:36:27 -05:00
"" ,
} ,
2015-09-08 14:49:54 -04:00
"invalid-name" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "^Invalid" , Namespace : "foo" } , Spec : api . LimitRangeSpec { } } ,
2016-12-01 23:32:41 -05:00
dnsSubdomainLabelErrMsg ,
2015-02-04 19:36:27 -05:00
} ,
2015-09-08 14:49:54 -04:00
"invalid-namespace" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "^Invalid" } , Spec : api . LimitRangeSpec { } } ,
2016-12-01 23:32:41 -05:00
dnsLabelErrMsg ,
2015-01-22 16:52:40 -05:00
} ,
2015-09-08 14:49:54 -04:00
"duplicate-limit-type" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-08 14:49:54 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getResourceList ( "100m" , "10000m" ) ,
Min : getResourceList ( "0m" , "100m" ) ,
} ,
{
Type : api . LimitTypePod ,
Min : getResourceList ( "0m" , "100m" ) ,
} ,
} ,
} } ,
2015-06-16 16:16:34 -04:00
"" ,
} ,
2015-09-10 16:39:59 -04:00
"default-limit-type-pod" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-10 16:39:59 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getResourceList ( "100m" , "10000m" ) ,
Min : getResourceList ( "0m" , "100m" ) ,
Default : getResourceList ( "10m" , "100m" ) ,
} ,
} ,
} } ,
2015-11-14 15:26:04 -05:00
"may not be specified when `type` is 'Pod'" ,
2015-09-10 16:39:59 -04:00
} ,
"default-request-limit-type-pod" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-10 16:39:59 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getResourceList ( "100m" , "10000m" ) ,
Min : getResourceList ( "0m" , "100m" ) ,
DefaultRequest : getResourceList ( "10m" , "100m" ) ,
} ,
} ,
} } ,
2015-11-14 15:26:04 -05:00
"may not be specified when `type` is 'Pod'" ,
2015-09-10 16:39:59 -04:00
} ,
2015-09-08 14:49:54 -04:00
"min value 100m is greater than max value 10m" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-08 14:49:54 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
Max : getResourceList ( "10m" , "" ) ,
Min : getResourceList ( "100m" , "" ) ,
} ,
} ,
} } ,
"min value 100m is greater than max value 10m" ,
2015-06-16 16:16:34 -04:00
} ,
"invalid spec default outside range" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-08 14:49:54 -04:00
Limits : [ ] api . LimitRangeItem {
{
2015-09-10 16:39:59 -04:00
Type : api . LimitTypeContainer ,
2015-09-08 14:49:54 -04:00
Max : getResourceList ( "1" , "" ) ,
Min : getResourceList ( "100m" , "" ) ,
Default : getResourceList ( "2000m" , "" ) ,
} ,
} ,
} } ,
"default value 2 is greater than max value 1" ,
2015-06-16 16:16:34 -04:00
} ,
2017-08-16 04:55:17 -04:00
"invalid spec default request outside range" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-08 14:49:54 -04:00
Limits : [ ] api . LimitRangeItem {
{
2015-09-10 16:39:59 -04:00
Type : api . LimitTypeContainer ,
2015-09-08 14:49:54 -04:00
Max : getResourceList ( "1" , "" ) ,
Min : getResourceList ( "100m" , "" ) ,
DefaultRequest : getResourceList ( "2000m" , "" ) ,
} ,
} ,
} } ,
"default request value 2 is greater than max value 1" ,
2015-08-28 12:26:36 -04:00
} ,
2017-08-16 04:55:17 -04:00
"invalid spec default request more than default" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-08 14:49:54 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypeContainer ,
Max : getResourceList ( "2" , "" ) ,
Min : getResourceList ( "100m" , "" ) ,
Default : getResourceList ( "500m" , "" ) ,
DefaultRequest : getResourceList ( "800m" , "" ) ,
} ,
} ,
} } ,
"default request value 800m is greater than default limit value 500m" ,
2015-08-28 12:26:36 -04:00
} ,
2015-09-11 03:38:38 -04:00
"invalid spec maxLimitRequestRatio less than 1" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-11 03:38:38 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePod ,
MaxLimitRequestRatio : getResourceList ( "800m" , "" ) ,
} ,
} ,
} } ,
2015-11-04 16:52:14 -05:00
"ratio 800m is less than 1" ,
2015-09-11 03:38:38 -04:00
} ,
"invalid spec maxLimitRequestRatio greater than max/min" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2015-09-11 03:38:38 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypeContainer ,
Max : getResourceList ( "" , "2Gi" ) ,
Min : getResourceList ( "" , "512Mi" ) ,
MaxLimitRequestRatio : getResourceList ( "" , "10" ) ,
} ,
} ,
} } ,
2015-11-04 16:52:14 -05:00
"ratio 10 is greater than max/min = 4.000000" ,
2015-09-11 03:38:38 -04:00
} ,
2016-03-09 18:11:26 -05:00
"invalid non standard limit type" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2016-03-09 18:11:26 -05:00
Limits : [ ] api . LimitRangeItem {
{
Type : "foo" ,
Max : getStorageResourceList ( "10000T" ) ,
Min : getStorageResourceList ( "100Mi" ) ,
Default : getStorageResourceList ( "500Mi" ) ,
DefaultRequest : getStorageResourceList ( "200Mi" ) ,
MaxLimitRequestRatio : getStorageResourceList ( "" ) ,
} ,
} ,
} } ,
"must be a standard limit type or fully qualified" ,
} ,
2016-10-24 11:01:18 -04:00
"min and max values missing, one required" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2016-08-15 10:19:15 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePersistentVolumeClaim ,
} ,
} ,
} } ,
2016-10-24 11:01:18 -04:00
"either minimum or maximum storage value is required, but neither was provided" ,
2016-08-15 10:19:15 -04:00
} ,
"invalid min greater than max" : {
2017-01-16 22:38:19 -05:00
api . LimitRange { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : api . LimitRangeSpec {
2016-08-15 10:19:15 -04:00
Limits : [ ] api . LimitRangeItem {
{
Type : api . LimitTypePersistentVolumeClaim ,
Min : getStorageResourceList ( "10Gi" ) ,
Max : getStorageResourceList ( "1Gi" ) ,
} ,
} ,
} } ,
"min value 10Gi is greater than max value 1Gi" ,
} ,
2015-01-22 16:52:40 -05:00
}
2015-09-08 14:49:54 -04:00
2015-01-22 16:52:40 -05:00
for k , v := range errorCases {
2015-02-04 19:36:27 -05:00
errs := ValidateLimitRange ( & v . R )
2015-01-22 16:52:40 -05:00
if len ( errs ) == 0 {
t . Errorf ( "expected failure for %s" , k )
}
for i := range errs {
2015-11-03 19:08:20 -05:00
detail := errs [ i ] . Detail
2015-12-16 02:49:58 -05:00
if ! strings . Contains ( detail , v . D ) {
2015-11-04 16:52:14 -05:00
t . Errorf ( "[%s]: expected error detail either empty or %q, got %q" , k , v . D , detail )
2015-02-04 19:36:27 -05:00
}
2015-01-22 16:52:40 -05:00
}
}
2015-09-08 14:49:54 -04:00
2015-01-22 16:52:40 -05:00
}
2015-01-23 12:38:30 -05:00
func TestValidateResourceQuota ( t * testing . T ) {
2015-02-04 19:36:27 -05:00
spec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
api . ResourceMemory : resource . MustParse ( "10000" ) ,
2016-02-22 11:14:11 -05:00
api . ResourceRequestsCPU : resource . MustParse ( "100" ) ,
api . ResourceRequestsMemory : resource . MustParse ( "10000" ) ,
api . ResourceLimitsCPU : resource . MustParse ( "100" ) ,
api . ResourceLimitsMemory : resource . MustParse ( "10000" ) ,
2015-02-04 19:36:27 -05:00
api . ResourcePods : resource . MustParse ( "10" ) ,
2015-09-24 10:15:40 -04:00
api . ResourceServices : resource . MustParse ( "0" ) ,
2015-02-04 19:36:27 -05:00
api . ResourceReplicationControllers : resource . MustParse ( "10" ) ,
api . ResourceQuotas : resource . MustParse ( "10" ) ,
2016-02-29 12:02:05 -05:00
api . ResourceConfigMaps : resource . MustParse ( "10" ) ,
api . ResourceSecrets : resource . MustParse ( "10" ) ,
2015-02-04 19:36:27 -05:00
} ,
}
2016-02-22 11:14:11 -05:00
terminatingSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
api . ResourceLimitsCPU : resource . MustParse ( "200" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeTerminating } ,
}
nonTerminatingSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeNotTerminating } ,
}
bestEffortSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourcePods : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeBestEffort } ,
}
nonBestEffortSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeNotBestEffort } ,
}
// storage is not yet supported as a quota tracked resource
invalidQuotaResourceSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceStorage : resource . MustParse ( "10" ) ,
} ,
}
2015-09-24 10:15:40 -04:00
negativeSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "-100" ) ,
api . ResourceMemory : resource . MustParse ( "-10000" ) ,
api . ResourcePods : resource . MustParse ( "-10" ) ,
api . ResourceServices : resource . MustParse ( "-10" ) ,
api . ResourceReplicationControllers : resource . MustParse ( "-10" ) ,
api . ResourceQuotas : resource . MustParse ( "-10" ) ,
2016-02-29 12:02:05 -05:00
api . ResourceConfigMaps : resource . MustParse ( "-10" ) ,
api . ResourceSecrets : resource . MustParse ( "-10" ) ,
2015-09-24 10:15:40 -04:00
} ,
}
2015-10-13 17:27:56 -04:00
fractionalComputeSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100m" ) ,
} ,
}
fractionalPodSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourcePods : resource . MustParse ( ".1" ) ,
api . ResourceServices : resource . MustParse ( ".5" ) ,
api . ResourceReplicationControllers : resource . MustParse ( "1.25" ) ,
api . ResourceQuotas : resource . MustParse ( "2.5" ) ,
} ,
}
2016-02-22 11:14:11 -05:00
invalidTerminatingScopePairsSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeTerminating , api . ResourceQuotaScopeNotTerminating } ,
}
invalidBestEffortScopePairsSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourcePods : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScopeBestEffort , api . ResourceQuotaScopeNotBestEffort } ,
}
invalidScopeNameSpec := api . ResourceQuotaSpec {
Hard : api . ResourceList {
api . ResourceCPU : resource . MustParse ( "100" ) ,
} ,
Scopes : [ ] api . ResourceQuotaScope { api . ResourceQuotaScope ( "foo" ) } ,
}
2015-01-23 12:38:30 -05:00
successCases := [ ] api . ResourceQuota {
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-01-23 12:38:30 -05:00
Name : "abc" ,
Namespace : "foo" ,
} ,
2015-02-04 19:36:27 -05:00
Spec : spec ,
2015-01-23 12:38:30 -05:00
} ,
2015-10-13 17:27:56 -04:00
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-10-13 17:27:56 -04:00
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : fractionalComputeSpec ,
} ,
2016-02-22 11:14:11 -05:00
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-22 11:14:11 -05:00
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : terminatingSpec ,
} ,
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-22 11:14:11 -05:00
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : nonTerminatingSpec ,
} ,
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-22 11:14:11 -05:00
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : bestEffortSpec ,
} ,
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-02-22 11:14:11 -05:00
Name : "abc" ,
Namespace : "foo" ,
} ,
Spec : nonBestEffortSpec ,
} ,
2015-01-23 12:38:30 -05:00
}
for _ , successCase := range successCases {
if errs := ValidateResourceQuota ( & successCase ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
}
2015-02-04 19:36:27 -05:00
errorCases := map [ string ] struct {
R api . ResourceQuota
D string
} {
2015-01-23 12:38:30 -05:00
"zero-length Name" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "" , Namespace : "foo" } , Spec : spec } ,
2015-08-12 11:26:23 -04:00
"name or generateName is required" ,
2015-01-23 12:38:30 -05:00
} ,
2015-02-04 19:36:27 -05:00
"zero-length Namespace" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "" } , Spec : spec } ,
2015-02-04 19:36:27 -05:00
"" ,
} ,
"invalid Name" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "^Invalid" , Namespace : "foo" } , Spec : spec } ,
2016-12-01 23:32:41 -05:00
dnsSubdomainLabelErrMsg ,
2015-02-04 19:36:27 -05:00
} ,
"invalid Namespace" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "^Invalid" } , Spec : spec } ,
2016-12-01 23:32:41 -05:00
dnsLabelErrMsg ,
2015-01-23 12:38:30 -05:00
} ,
2015-09-24 10:15:40 -04:00
"negative-limits" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : negativeSpec } ,
2015-09-24 10:15:40 -04:00
isNegativeErrorMsg ,
} ,
2015-10-13 17:27:56 -04:00
"fractional-api-resource" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : fractionalPodSpec } ,
2015-10-13 17:27:56 -04:00
isNotIntegerErrorMsg ,
} ,
2016-02-22 11:14:11 -05:00
"invalid-quota-resource" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : invalidQuotaResourceSpec } ,
2016-02-22 11:14:11 -05:00
isInvalidQuotaResource ,
} ,
"invalid-quota-terminating-pair" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : invalidTerminatingScopePairsSpec } ,
2016-02-22 11:14:11 -05:00
"conflicting scopes" ,
} ,
"invalid-quota-besteffort-pair" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : invalidBestEffortScopePairsSpec } ,
2016-02-22 11:14:11 -05:00
"conflicting scopes" ,
} ,
"invalid-quota-scope-name" : {
2017-01-16 22:38:19 -05:00
api . ResourceQuota { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Namespace : "foo" } , Spec : invalidScopeNameSpec } ,
2016-02-22 11:14:11 -05:00
"unsupported scope" ,
} ,
2015-01-23 12:38:30 -05:00
}
for k , v := range errorCases {
2015-02-04 19:36:27 -05:00
errs := ValidateResourceQuota ( & v . R )
2015-01-23 12:38:30 -05:00
if len ( errs ) == 0 {
t . Errorf ( "expected failure for %s" , k )
}
for i := range errs {
2015-12-16 02:49:58 -05:00
if ! strings . Contains ( errs [ i ] . Detail , v . D ) {
2015-11-04 16:52:14 -05:00
t . Errorf ( "[%s]: expected error detail either empty or %s, got %s" , k , v . D , errs [ i ] . Detail )
2015-02-04 19:36:27 -05:00
}
2015-01-23 12:38:30 -05:00
}
}
}
2015-01-19 16:50:00 -05:00
func TestValidateNamespace ( t * testing . T ) {
validLabels := map [ string ] string { "a" : "b" }
invalidLabels := map [ string ] string { "NoUppercaseOrSpecialCharsLike=Equals" : "b" }
successCases := [ ] api . Namespace {
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Labels : validLabels } ,
2015-01-19 16:50:00 -05:00
} ,
{
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "abc-123" } ,
2015-03-31 17:00:04 -04:00
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "example.com/something" , "example.com/other" } ,
} ,
2015-01-19 16:50:00 -05:00
} ,
}
for _ , successCase := range successCases {
if errs := ValidateNamespace ( & successCase ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
}
errorCases := map [ string ] struct {
R api . Namespace
D string
} {
"zero-length name" : {
2017-01-16 22:38:19 -05:00
api . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "" } } ,
2015-01-19 16:50:00 -05:00
"" ,
} ,
"defined-namespace" : {
2017-01-16 22:38:19 -05:00
api . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "abc-123" , Namespace : "makesnosense" } } ,
2015-01-19 16:50:00 -05:00
"" ,
} ,
"invalid-labels" : {
2017-01-16 22:38:19 -05:00
api . Namespace { ObjectMeta : metav1 . ObjectMeta { Name : "abc" , Labels : invalidLabels } } ,
2015-01-19 16:50:00 -05:00
"" ,
} ,
}
for k , v := range errorCases {
errs := ValidateNamespace ( & v . R )
if len ( errs ) == 0 {
t . Errorf ( "expected failure for %s" , k )
}
}
}
2015-03-20 12:48:12 -04:00
func TestValidateNamespaceFinalizeUpdate ( t * testing . T ) {
tests := [ ] struct {
oldNamespace api . Namespace
namespace api . Namespace
valid bool
} {
{ api . Namespace { } , api . Namespace { } , true } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } } ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "Foo" } ,
} ,
} , false } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "foo.com/bar" } ,
} ,
} ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "foo.com/bar" , "what.com/bar" } ,
} ,
} , true } ,
2015-03-31 17:00:04 -04:00
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "fooemptyfinalizer" } ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "foo.com/bar" } ,
} ,
} ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "fooemptyfinalizer" } ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "" , "foo.com/bar" , "what.com/bar" } ,
} ,
} , false } ,
2015-03-20 12:48:12 -04:00
}
for i , test := range tests {
2015-03-19 20:51:07 -04:00
test . namespace . ObjectMeta . ResourceVersion = "1"
test . oldNamespace . ObjectMeta . ResourceVersion = "1"
2015-03-20 12:48:12 -04:00
errs := ValidateNamespaceFinalizeUpdate ( & test . namespace , & test . oldNamespace )
if test . valid && len ( errs ) > 0 {
t . Errorf ( "%d: Unexpected error: %v" , i , errs )
t . Logf ( "%#v vs %#v" , test . oldNamespace , test . namespace )
}
if ! test . valid && len ( errs ) == 0 {
t . Errorf ( "%d: Unexpected non-error" , i )
}
}
}
func TestValidateNamespaceStatusUpdate ( t * testing . T ) {
2016-12-03 13:57:26 -05:00
now := metav1 . Now ( )
2015-04-10 11:41:18 -04:00
2015-03-20 12:48:12 -04:00
tests := [ ] struct {
oldNamespace api . Namespace
namespace api . Namespace
valid bool
} {
2015-04-10 11:41:18 -04:00
{ api . Namespace { } , api . Namespace {
Status : api . NamespaceStatus {
Phase : api . NamespaceActive ,
} ,
} , true } ,
2016-04-28 11:50:48 -04:00
// Cannot set deletionTimestamp via status update
2015-03-20 12:48:12 -04:00
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } } ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-10 11:41:18 -04:00
Name : "foo" ,
DeletionTimestamp : & now } ,
2015-03-20 12:48:12 -04:00
Status : api . NamespaceStatus {
Phase : api . NamespaceTerminating ,
} ,
2016-04-28 11:50:48 -04:00
} , false } ,
// Can update phase via status update
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-04-28 11:50:48 -04:00
Name : "foo" ,
DeletionTimestamp : & now } } ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-04-28 11:50:48 -04:00
Name : "foo" ,
DeletionTimestamp : & now } ,
Status : api . NamespaceStatus {
Phase : api . NamespaceTerminating ,
} ,
2015-03-20 12:48:12 -04:00
} , true } ,
2015-04-10 11:41:18 -04:00
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-10 11:41:18 -04:00
Name : "foo" } } ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-10 11:41:18 -04:00
Name : "foo" } ,
Status : api . NamespaceStatus {
Phase : api . NamespaceTerminating ,
} ,
} , false } ,
2015-03-20 12:48:12 -04:00
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "foo" } } ,
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-20 12:48:12 -04:00
Name : "bar" } ,
Status : api . NamespaceStatus {
Phase : api . NamespaceTerminating ,
} ,
} , false } ,
}
for i , test := range tests {
2015-03-19 20:51:07 -04:00
test . namespace . ObjectMeta . ResourceVersion = "1"
test . oldNamespace . ObjectMeta . ResourceVersion = "1"
2015-04-10 11:41:18 -04:00
errs := ValidateNamespaceStatusUpdate ( & test . namespace , & test . oldNamespace )
2015-03-20 12:48:12 -04:00
if test . valid && len ( errs ) > 0 {
t . Errorf ( "%d: Unexpected error: %v" , i , errs )
t . Logf ( "%#v vs %#v" , test . oldNamespace . ObjectMeta , test . namespace . ObjectMeta )
}
if ! test . valid && len ( errs ) == 0 {
t . Errorf ( "%d: Unexpected non-error" , i )
}
}
}
2015-01-19 16:50:00 -05:00
func TestValidateNamespaceUpdate ( t * testing . T ) {
tests := [ ] struct {
oldNamespace api . Namespace
namespace api . Namespace
valid bool
} {
{ api . Namespace { } , api . Namespace { } , true } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo1" } } ,
2015-01-19 16:50:00 -05:00
api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "bar1" } ,
2015-01-19 16:50:00 -05:00
} , false } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo2" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "foo" : "bar" } ,
} ,
} , api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo2" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo3" ,
2015-01-19 16:50:00 -05:00
} ,
} , api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo3" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo4" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "bar" : "foo" } ,
} ,
} , api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo4" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , true } ,
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo5" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo5" ,
2015-01-19 16:50:00 -05:00
Labels : map [ string ] string { "Foo" : "baz" } ,
} ,
2015-02-27 10:08:02 -05:00
} , true } ,
2015-03-31 17:00:04 -04:00
{ api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo6" ,
Labels : map [ string ] string { "foo" : "baz" } ,
} ,
} , api . Namespace {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-03-31 17:00:04 -04:00
Name : "foo6" ,
Labels : map [ string ] string { "Foo" : "baz" } ,
} ,
Spec : api . NamespaceSpec {
Finalizers : [ ] api . FinalizerName { "kubernetes" } ,
} ,
Status : api . NamespaceStatus {
Phase : api . NamespaceTerminating ,
} ,
} , true } ,
2015-01-19 16:50:00 -05:00
}
for i , test := range tests {
2015-03-19 20:51:07 -04:00
test . namespace . ObjectMeta . ResourceVersion = "1"
test . oldNamespace . ObjectMeta . ResourceVersion = "1"
2015-03-31 17:00:04 -04:00
errs := ValidateNamespaceUpdate ( & test . namespace , & test . oldNamespace )
2015-01-19 16:50:00 -05:00
if test . valid && len ( errs ) > 0 {
t . Errorf ( "%d: Unexpected error: %v" , i , errs )
t . Logf ( "%#v vs %#v" , test . oldNamespace . ObjectMeta , test . namespace . ObjectMeta )
}
if ! test . valid && len ( errs ) == 0 {
t . Errorf ( "%d: Unexpected non-error" , i )
}
}
}
2015-02-17 20:24:50 -05:00
func TestValidateSecret ( t * testing . T ) {
2015-04-27 23:51:20 -04:00
// Opaque secret validation
2015-02-17 20:24:50 -05:00
validSecret := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "bar" } ,
2015-02-17 20:24:50 -05:00
Data : map [ string ] [ ] byte {
2015-02-17 20:26:41 -05:00
"data-1" : [ ] byte ( "bar" ) ,
2015-02-17 20:24:50 -05:00
} ,
}
}
var (
2015-05-11 15:03:10 -04:00
emptyName = validSecret ( )
invalidName = validSecret ( )
emptyNs = validSecret ( )
invalidNs = validSecret ( )
overMaxSize = validSecret ( )
invalidKey = validSecret ( )
leadingDotKey = validSecret ( )
dotKey = validSecret ( )
doubleDotKey = validSecret ( )
2015-02-17 20:24:50 -05:00
)
emptyName . Name = ""
invalidName . Name = "NoUppercaseOrSpecialCharsLike=Equals"
emptyNs . Namespace = ""
invalidNs . Namespace = "NoUppercaseOrSpecialCharsLike=Equals"
overMaxSize . Data = map [ string ] [ ] byte {
"over" : make ( [ ] byte , api . MaxSecretSize + 1 ) ,
}
2016-05-11 01:59:59 -04:00
invalidKey . Data [ "a*b" ] = [ ] byte ( "whoops" )
2015-05-11 15:03:10 -04:00
leadingDotKey . Data [ ".key" ] = [ ] byte ( "bar" )
dotKey . Data [ "." ] = [ ] byte ( "bar" )
doubleDotKey . Data [ ".." ] = [ ] byte ( "bar" )
2015-02-17 20:24:50 -05:00
2015-04-27 23:51:20 -04:00
// kubernetes.io/service-account-token secret validation
validServiceAccountTokenSecret := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-27 23:51:20 -04:00
Name : "foo" ,
Namespace : "bar" ,
Annotations : map [ string ] string {
api . ServiceAccountNameKey : "foo" ,
} ,
} ,
Type : api . SecretTypeServiceAccountToken ,
Data : map [ string ] [ ] byte {
"data-1" : [ ] byte ( "bar" ) ,
} ,
}
}
var (
emptyTokenAnnotation = validServiceAccountTokenSecret ( )
missingTokenAnnotation = validServiceAccountTokenSecret ( )
missingTokenAnnotations = validServiceAccountTokenSecret ( )
)
emptyTokenAnnotation . Annotations [ api . ServiceAccountNameKey ] = ""
delete ( missingTokenAnnotation . Annotations , api . ServiceAccountNameKey )
missingTokenAnnotations . Annotations = nil
2015-02-17 20:24:50 -05:00
tests := map [ string ] struct {
secret api . Secret
valid bool
} {
2015-05-11 15:03:10 -04:00
"valid" : { validSecret ( ) , true } ,
"empty name" : { emptyName , false } ,
"invalid name" : { invalidName , false } ,
"empty namespace" : { emptyNs , false } ,
"invalid namespace" : { invalidNs , false } ,
"over max size" : { overMaxSize , false } ,
"invalid key" : { invalidKey , false } ,
2015-04-27 23:51:20 -04:00
"valid service-account-token secret" : { validServiceAccountTokenSecret ( ) , true } ,
"empty service-account-token annotation" : { emptyTokenAnnotation , false } ,
"missing service-account-token annotation" : { missingTokenAnnotation , false } ,
"missing service-account-token annotations" : { missingTokenAnnotations , false } ,
2015-05-11 15:03:10 -04:00
"leading dot key" : { leadingDotKey , true } ,
"dot key" : { dotKey , false } ,
"double dot key" : { doubleDotKey , false } ,
2015-02-17 20:24:50 -05:00
}
for name , tc := range tests {
errs := ValidateSecret ( & tc . secret )
2015-05-06 10:09:18 -04:00
if tc . valid && len ( errs ) > 0 {
t . Errorf ( "%v: Unexpected error: %v" , name , errs )
}
if ! tc . valid && len ( errs ) == 0 {
t . Errorf ( "%v: Unexpected non-error" , name )
}
}
}
func TestValidateDockerConfigSecret ( t * testing . T ) {
validDockerSecret := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "bar" } ,
2015-05-06 10:09:18 -04:00
Type : api . SecretTypeDockercfg ,
Data : map [ string ] [ ] byte {
api . DockerConfigKey : [ ] byte ( ` { "https://index.docker.io/v1/": { "auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}} ` ) ,
} ,
}
}
2015-12-16 17:13:18 -05:00
validDockerSecret2 := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "bar" } ,
2015-12-16 17:13:18 -05:00
Type : api . SecretTypeDockerConfigJson ,
Data : map [ string ] [ ] byte {
api . DockerConfigJsonKey : [ ] byte ( ` { "auths": { "https://index.docker.io/v1/": { "auth": "Y2x1ZWRyb29sZXIwMDAxOnBhc3N3b3Jk","email": "fake@example.com"}}} ` ) ,
} ,
}
}
2015-05-06 10:09:18 -04:00
var (
2015-12-16 17:13:18 -05:00
missingDockerConfigKey = validDockerSecret ( )
emptyDockerConfigKey = validDockerSecret ( )
invalidDockerConfigKey = validDockerSecret ( )
missingDockerConfigKey2 = validDockerSecret2 ( )
emptyDockerConfigKey2 = validDockerSecret2 ( )
invalidDockerConfigKey2 = validDockerSecret2 ( )
2015-05-06 10:09:18 -04:00
)
delete ( missingDockerConfigKey . Data , api . DockerConfigKey )
emptyDockerConfigKey . Data [ api . DockerConfigKey ] = [ ] byte ( "" )
invalidDockerConfigKey . Data [ api . DockerConfigKey ] = [ ] byte ( "bad" )
2015-12-16 17:13:18 -05:00
delete ( missingDockerConfigKey2 . Data , api . DockerConfigJsonKey )
emptyDockerConfigKey2 . Data [ api . DockerConfigJsonKey ] = [ ] byte ( "" )
invalidDockerConfigKey2 . Data [ api . DockerConfigJsonKey ] = [ ] byte ( "bad" )
2015-05-06 10:09:18 -04:00
tests := map [ string ] struct {
secret api . Secret
valid bool
} {
2015-12-16 17:13:18 -05:00
"valid dockercfg" : { validDockerSecret ( ) , true } ,
"missing dockercfg" : { missingDockerConfigKey , false } ,
"empty dockercfg" : { emptyDockerConfigKey , false } ,
"invalid dockercfg" : { invalidDockerConfigKey , false } ,
"valid config.json" : { validDockerSecret2 ( ) , true } ,
"missing config.json" : { missingDockerConfigKey2 , false } ,
"empty config.json" : { emptyDockerConfigKey2 , false } ,
"invalid config.json" : { invalidDockerConfigKey2 , false } ,
2015-05-06 10:09:18 -04:00
}
2015-09-10 11:30:58 -04:00
for name , tc := range tests {
errs := ValidateSecret ( & tc . secret )
if tc . valid && len ( errs ) > 0 {
t . Errorf ( "%v: Unexpected error: %v" , name , errs )
}
if ! tc . valid && len ( errs ) == 0 {
t . Errorf ( "%v: Unexpected non-error" , name )
}
}
}
func TestValidateBasicAuthSecret ( t * testing . T ) {
validBasicAuthSecret := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "bar" } ,
2015-09-10 11:30:58 -04:00
Type : api . SecretTypeBasicAuth ,
Data : map [ string ] [ ] byte {
api . BasicAuthUsernameKey : [ ] byte ( "username" ) ,
api . BasicAuthPasswordKey : [ ] byte ( "password" ) ,
} ,
}
}
var (
missingBasicAuthUsernamePasswordKeys = validBasicAuthSecret ( )
)
delete ( missingBasicAuthUsernamePasswordKeys . Data , api . BasicAuthUsernameKey )
delete ( missingBasicAuthUsernamePasswordKeys . Data , api . BasicAuthPasswordKey )
tests := map [ string ] struct {
secret api . Secret
valid bool
} {
"valid" : { validBasicAuthSecret ( ) , true } ,
"missing username and password" : { missingBasicAuthUsernamePasswordKeys , false } ,
}
for name , tc := range tests {
errs := ValidateSecret ( & tc . secret )
if tc . valid && len ( errs ) > 0 {
t . Errorf ( "%v: Unexpected error: %v" , name , errs )
}
if ! tc . valid && len ( errs ) == 0 {
t . Errorf ( "%v: Unexpected non-error" , name )
}
}
}
func TestValidateSSHAuthSecret ( t * testing . T ) {
validSSHAuthSecret := func ( ) api . Secret {
return api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : "bar" } ,
2015-09-10 11:30:58 -04:00
Type : api . SecretTypeSSHAuth ,
Data : map [ string ] [ ] byte {
api . SSHAuthPrivateKey : [ ] byte ( "foo-bar-baz" ) ,
} ,
}
}
missingSSHAuthPrivateKey := validSSHAuthSecret ( )
delete ( missingSSHAuthPrivateKey . Data , api . SSHAuthPrivateKey )
tests := map [ string ] struct {
secret api . Secret
valid bool
} {
"valid" : { validSSHAuthSecret ( ) , true } ,
"missing private key" : { missingSSHAuthPrivateKey , false } ,
}
2015-05-06 10:09:18 -04:00
for name , tc := range tests {
errs := ValidateSecret ( & tc . secret )
2015-02-17 20:24:50 -05:00
if tc . valid && len ( errs ) > 0 {
t . Errorf ( "%v: Unexpected error: %v" , name , errs )
}
if ! tc . valid && len ( errs ) == 0 {
t . Errorf ( "%v: Unexpected non-error" , name )
}
}
}
2015-03-20 17:24:43 -04:00
func TestValidateEndpoints ( t * testing . T ) {
2015-03-20 10:40:20 -04:00
successCases := map [ string ] api . Endpoints {
"simple endpoint" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } , { IP : "10.10.2.2" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 8675 , Protocol : "TCP" } , { Name : "b" , Port : 309 , Protocol : "TCP" } } ,
} ,
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.3.3" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 , Protocol : "TCP" } , { Name : "b" , Port : 76 , Protocol : "TCP" } } ,
} ,
} ,
} ,
"empty subsets" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
} ,
"no name required for singleton port" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Port : 8675 , Protocol : "TCP" } } ,
} ,
} ,
} ,
2017-06-09 11:22:37 -04:00
"empty ports" : {
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.3.3" } } ,
} ,
} ,
} ,
2015-03-20 10:40:20 -04:00
}
for k , v := range successCases {
if errs := ValidateEndpoints ( & v ) ; len ( errs ) != 0 {
t . Errorf ( "Expected success for %s, got %v" , k , errs )
}
}
errorCases := map [ string ] struct {
endpoints api . Endpoints
2015-11-06 18:30:52 -05:00
errorType field . ErrorType
2015-03-20 10:40:20 -04:00
errorDetail string
} {
"missing namespace" : {
2017-01-16 22:38:19 -05:00
endpoints : api . Endpoints { ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" } } ,
2015-03-20 10:40:20 -04:00
errorType : "FieldValueRequired" ,
} ,
"missing name" : {
2017-01-16 22:38:19 -05:00
endpoints : api . Endpoints { ObjectMeta : metav1 . ObjectMeta { Namespace : "namespace" } } ,
2015-03-20 10:40:20 -04:00
errorType : "FieldValueRequired" ,
} ,
"invalid namespace" : {
2017-01-16 22:38:19 -05:00
endpoints : api . Endpoints { ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "no@#invalid.;chars\"allowed" } } ,
2015-03-20 10:40:20 -04:00
errorType : "FieldValueInvalid" ,
2016-12-01 23:32:41 -05:00
errorDetail : dnsLabelErrMsg ,
2015-03-20 10:40:20 -04:00
} ,
"invalid name" : {
2017-01-16 22:38:19 -05:00
endpoints : api . Endpoints { ObjectMeta : metav1 . ObjectMeta { Name : "-_Invliad^&Characters" , Namespace : "namespace" } } ,
2015-03-20 10:40:20 -04:00
errorType : "FieldValueInvalid" ,
2016-12-01 23:32:41 -05:00
errorDetail : dnsSubdomainLabelErrMsg ,
2015-03-20 10:40:20 -04:00
} ,
"empty addresses" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueRequired" ,
} ,
"invalid IP" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
2016-03-22 00:54:32 -04:00
Addresses : [ ] api . EndpointAddress { { IP : "[2001:0db8:85a3:0042:1000:8a2e:0370:7334]" } } ,
2015-03-20 10:40:20 -04:00
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
2016-03-22 00:54:32 -04:00
errorDetail : "must be a valid IP address" ,
2015-03-20 10:40:20 -04:00
} ,
"Multiple ports, one without name" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Port : 8675 , Protocol : "TCP" } , { Name : "b" , Port : 309 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueRequired" ,
} ,
"Invalid port number" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 66000 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
2016-01-04 11:33:26 -05:00
errorDetail : "between" ,
2015-03-20 10:40:20 -04:00
} ,
"Invalid protocol" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 , Protocol : "Protocol" } } ,
} ,
} ,
} ,
errorType : "FieldValueNotSupported" ,
} ,
"Address missing IP" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
2016-03-22 00:54:32 -04:00
errorDetail : "must be a valid IP address" ,
2015-03-20 10:40:20 -04:00
} ,
"Port missing number" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
2016-01-04 11:33:26 -05:00
errorDetail : "between" ,
2015-03-20 10:40:20 -04:00
} ,
"Port missing protocol" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-03-20 10:40:20 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "10.10.1.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "a" , Port : 93 } } ,
} ,
} ,
} ,
errorType : "FieldValueRequired" ,
} ,
2015-07-04 00:05:15 -04:00
"Address is loopback" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-07-04 00:05:15 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "127.0.0.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "p" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
errorDetail : "loopback" ,
} ,
2015-06-03 00:49:51 -04:00
"Address is link-local" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-06-03 00:49:51 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "169.254.169.254" } } ,
Ports : [ ] api . EndpointPort { { Name : "p" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
errorDetail : "link-local" ,
} ,
2015-07-04 00:05:15 -04:00
"Address is link-local multicast" : {
endpoints : api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "mysvc" , Namespace : "namespace" } ,
2015-07-04 00:05:15 -04:00
Subsets : [ ] api . EndpointSubset {
{
Addresses : [ ] api . EndpointAddress { { IP : "224.0.0.1" } } ,
Ports : [ ] api . EndpointPort { { Name : "p" , Port : 93 , Protocol : "TCP" } } ,
} ,
} ,
} ,
errorType : "FieldValueInvalid" ,
errorDetail : "link-local multicast" ,
} ,
2015-03-20 10:40:20 -04:00
}
for k , v := range errorCases {
2015-11-03 19:08:20 -05:00
if errs := ValidateEndpoints ( & v . endpoints ) ; len ( errs ) == 0 || errs [ 0 ] . Type != v . errorType || ! strings . Contains ( errs [ 0 ] . Detail , v . errorDetail ) {
2015-11-14 15:26:04 -05:00
t . Errorf ( "[%s] Expected error type %s with detail %q, got %v" , k , v . errorType , v . errorDetail , errs )
2015-03-20 10:40:20 -04:00
}
}
2015-03-20 17:24:43 -04:00
}
2015-05-05 19:02:13 -04:00
2016-01-16 19:06:40 -05:00
func TestValidateTLSSecret ( t * testing . T ) {
successCases := map [ string ] api . Secret {
2017-05-27 10:37:56 -04:00
"empty certificate chain" : {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "tls-cert" , Namespace : "namespace" } ,
2016-01-16 19:06:40 -05:00
Data : map [ string ] [ ] byte {
api . TLSCertKey : [ ] byte ( "public key" ) ,
api . TLSPrivateKeyKey : [ ] byte ( "private key" ) ,
} ,
} ,
}
for k , v := range successCases {
if errs := ValidateSecret ( & v ) ; len ( errs ) != 0 {
t . Errorf ( "Expected success for %s, got %v" , k , errs )
}
}
errorCases := map [ string ] struct {
secrets api . Secret
errorType field . ErrorType
errorDetail string
} {
"missing public key" : {
secrets : api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "tls-cert" } ,
2016-01-16 19:06:40 -05:00
Data : map [ string ] [ ] byte {
api . TLSCertKey : [ ] byte ( "public key" ) ,
} ,
} ,
errorType : "FieldValueRequired" ,
} ,
"missing private key" : {
secrets : api . Secret {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "tls-cert" } ,
2016-01-16 19:06:40 -05:00
Data : map [ string ] [ ] byte {
api . TLSCertKey : [ ] byte ( "public key" ) ,
} ,
} ,
errorType : "FieldValueRequired" ,
} ,
}
for k , v := range errorCases {
if errs := ValidateSecret ( & v . secrets ) ; len ( errs ) == 0 || errs [ 0 ] . Type != v . errorType || ! strings . Contains ( errs [ 0 ] . Detail , v . errorDetail ) {
t . Errorf ( "[%s] Expected error type %s with detail %q, got %v" , k , v . errorType , v . errorDetail , errs )
}
}
}
2015-05-05 19:02:13 -04:00
func TestValidateSecurityContext ( t * testing . T ) {
priv := false
2017-06-21 03:13:36 -04:00
runAsUser := int64 ( 1 )
2015-05-05 19:02:13 -04:00
fullValidSC := func ( ) * api . SecurityContext {
return & api . SecurityContext {
Privileged : & priv ,
Capabilities : & api . Capabilities {
2015-05-18 16:37:10 -04:00
Add : [ ] api . Capability { "foo" } ,
Drop : [ ] api . Capability { "bar" } ,
2015-05-05 19:02:13 -04:00
} ,
SELinuxOptions : & api . SELinuxOptions {
User : "user" ,
Role : "role" ,
Type : "type" ,
Level : "level" ,
} ,
RunAsUser : & runAsUser ,
}
}
//setup data
allSettings := fullValidSC ( )
noCaps := fullValidSC ( )
noCaps . Capabilities = nil
noSELinux := fullValidSC ( )
noSELinux . SELinuxOptions = nil
noPrivRequest := fullValidSC ( )
noPrivRequest . Privileged = nil
noRunAsUser := fullValidSC ( )
noRunAsUser . RunAsUser = nil
successCases := map [ string ] struct {
sc * api . SecurityContext
} {
"all settings" : { allSettings } ,
"no capabilities" : { noCaps } ,
"no selinux" : { noSELinux } ,
"no priv request" : { noPrivRequest } ,
"no run as user" : { noRunAsUser } ,
}
for k , v := range successCases {
2015-11-06 18:30:52 -05:00
if errs := ValidateSecurityContext ( v . sc , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
2015-11-14 15:26:04 -05:00
t . Errorf ( "[%s] Expected success, got %v" , k , errs )
2015-05-05 19:02:13 -04:00
}
}
privRequestWithGlobalDeny := fullValidSC ( )
requestPrivileged := true
privRequestWithGlobalDeny . Privileged = & requestPrivileged
negativeRunAsUser := fullValidSC ( )
2017-06-21 03:13:36 -04:00
negativeUser := int64 ( - 1 )
2015-05-05 19:02:13 -04:00
negativeRunAsUser . RunAsUser = & negativeUser
errorCases := map [ string ] struct {
sc * api . SecurityContext
2015-11-06 18:30:52 -05:00
errorType field . ErrorType
2015-05-05 19:02:13 -04:00
errorDetail string
} {
"request privileged when capabilities forbids" : {
sc : privRequestWithGlobalDeny ,
errorType : "FieldValueForbidden" ,
2017-01-05 12:07:50 -05:00
errorDetail : "disallowed by cluster policy" ,
2015-05-05 19:02:13 -04:00
} ,
"negative RunAsUser" : {
sc : negativeRunAsUser ,
errorType : "FieldValueInvalid" ,
2015-11-14 15:26:04 -05:00
errorDetail : isNegativeErrorMsg ,
2015-05-05 19:02:13 -04:00
} ,
}
for k , v := range errorCases {
2015-11-14 11:44:50 -05:00
if errs := ValidateSecurityContext ( v . sc , field . NewPath ( "field" ) ) ; len ( errs ) == 0 || errs [ 0 ] . Type != v . errorType || ! strings . Contains ( errs [ 0 ] . Detail , v . errorDetail ) {
t . Errorf ( "[%s] Expected error type %q with detail %q, got %v" , k , v . errorType , v . errorDetail , errs )
2015-05-05 19:02:13 -04:00
}
}
}
func fakeValidSecurityContext ( priv bool ) * api . SecurityContext {
return & api . SecurityContext {
Privileged : & priv ,
}
}
2015-09-09 23:46:11 -04:00
func TestValidPodLogOptions ( t * testing . T ) {
2016-12-03 13:57:26 -05:00
now := metav1 . Now ( )
2015-09-09 23:46:11 -04:00
negative := int64 ( - 1 )
zero := int64 ( 0 )
positive := int64 ( 1 )
tests := [ ] struct {
opt api . PodLogOptions
errs int
} {
{ api . PodLogOptions { } , 0 } ,
{ api . PodLogOptions { Previous : true } , 0 } ,
{ api . PodLogOptions { Follow : true } , 0 } ,
{ api . PodLogOptions { TailLines : & zero } , 0 } ,
{ api . PodLogOptions { TailLines : & negative } , 1 } ,
{ api . PodLogOptions { TailLines : & positive } , 0 } ,
{ api . PodLogOptions { LimitBytes : & zero } , 1 } ,
{ api . PodLogOptions { LimitBytes : & negative } , 1 } ,
{ api . PodLogOptions { LimitBytes : & positive } , 0 } ,
{ api . PodLogOptions { SinceSeconds : & negative } , 1 } ,
{ api . PodLogOptions { SinceSeconds : & positive } , 0 } ,
{ api . PodLogOptions { SinceSeconds : & zero } , 1 } ,
{ api . PodLogOptions { SinceTime : & now } , 0 } ,
}
for i , test := range tests {
errs := ValidatePodLogOptions ( & test . opt )
if test . errs != len ( errs ) {
t . Errorf ( "%d: Unexpected errors: %v" , i , errs )
}
}
}
2016-01-15 11:48:36 -05:00
func TestValidateConfigMap ( t * testing . T ) {
newConfigMap := func ( name , namespace string , data map [ string ] string ) api . ConfigMap {
return api . ConfigMap {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-15 11:48:36 -05:00
Name : name ,
Namespace : namespace ,
} ,
Data : data ,
}
}
var (
validConfigMap = newConfigMap ( "validname" , "validns" , map [ string ] string { "key" : "value" } )
maxKeyLength = newConfigMap ( "validname" , "validns" , map [ string ] string { strings . Repeat ( "a" , 253 ) : "value" } )
emptyName = newConfigMap ( "" , "validns" , nil )
invalidName = newConfigMap ( "NoUppercaseOrSpecialCharsLike=Equals" , "validns" , nil )
emptyNs = newConfigMap ( "validname" , "" , nil )
invalidNs = newConfigMap ( "validname" , "NoUppercaseOrSpecialCharsLike=Equals" , nil )
2016-05-11 01:59:59 -04:00
invalidKey = newConfigMap ( "validname" , "validns" , map [ string ] string { "a*b" : "value" } )
2016-01-15 11:48:36 -05:00
leadingDotKey = newConfigMap ( "validname" , "validns" , map [ string ] string { ".ab" : "value" } )
dotKey = newConfigMap ( "validname" , "validns" , map [ string ] string { "." : "value" } )
doubleDotKey = newConfigMap ( "validname" , "validns" , map [ string ] string { ".." : "value" } )
overMaxKeyLength = newConfigMap ( "validname" , "validns" , map [ string ] string { strings . Repeat ( "a" , 254 ) : "value" } )
2016-01-20 23:14:37 -05:00
overMaxSize = newConfigMap ( "validname" , "validns" , map [ string ] string { "key" : strings . Repeat ( "a" , api . MaxSecretSize + 1 ) } )
2016-01-15 11:48:36 -05:00
)
tests := map [ string ] struct {
cfg api . ConfigMap
isValid bool
} {
"valid" : { validConfigMap , true } ,
"max key length" : { maxKeyLength , true } ,
"leading dot key" : { leadingDotKey , true } ,
"empty name" : { emptyName , false } ,
"invalid name" : { invalidName , false } ,
"invalid key" : { invalidKey , false } ,
"empty namespace" : { emptyNs , false } ,
"invalid namespace" : { invalidNs , false } ,
"dot key" : { dotKey , false } ,
"double dot key" : { doubleDotKey , false } ,
"over max key length" : { overMaxKeyLength , false } ,
2016-01-20 23:14:37 -05:00
"over max size" : { overMaxSize , false } ,
2016-01-15 11:48:36 -05:00
}
for name , tc := range tests {
errs := ValidateConfigMap ( & tc . cfg )
if tc . isValid && len ( errs ) > 0 {
t . Errorf ( "%v: unexpected error: %v" , name , errs )
}
if ! tc . isValid && len ( errs ) == 0 {
t . Errorf ( "%v: unexpected non-error" , name )
}
}
}
func TestValidateConfigMapUpdate ( t * testing . T ) {
newConfigMap := func ( version , name , namespace string , data map [ string ] string ) api . ConfigMap {
return api . ConfigMap {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-15 11:48:36 -05:00
Name : name ,
Namespace : namespace ,
ResourceVersion : version ,
} ,
Data : data ,
}
}
var (
validConfigMap = newConfigMap ( "1" , "validname" , "validns" , map [ string ] string { "key" : "value" } )
noVersion = newConfigMap ( "" , "validname" , "validns" , map [ string ] string { "key" : "value" } )
)
cases := [ ] struct {
name string
newCfg api . ConfigMap
oldCfg api . ConfigMap
isValid bool
} {
{
name : "valid" ,
newCfg : validConfigMap ,
oldCfg : validConfigMap ,
isValid : true ,
} ,
{
name : "invalid" ,
newCfg : noVersion ,
oldCfg : validConfigMap ,
isValid : false ,
} ,
}
for _ , tc := range cases {
errs := ValidateConfigMapUpdate ( & tc . newCfg , & tc . oldCfg )
if tc . isValid && len ( errs ) > 0 {
t . Errorf ( "%v: unexpected error: %v" , tc . name , errs )
}
if ! tc . isValid && len ( errs ) == 0 {
t . Errorf ( "%v: unexpected non-error" , tc . name )
}
}
}
2016-02-12 17:38:22 -05:00
func TestValidateHasLabel ( t * testing . T ) {
2017-01-16 22:38:19 -05:00
successCase := metav1 . ObjectMeta {
2016-02-12 17:38:22 -05:00
Name : "123" ,
Namespace : "ns" ,
Labels : map [ string ] string {
"other" : "blah" ,
"foo" : "bar" ,
} ,
}
if errs := ValidateHasLabel ( successCase , field . NewPath ( "field" ) , "foo" , "bar" ) ; len ( errs ) != 0 {
t . Errorf ( "expected success: %v" , errs )
}
2017-01-16 22:38:19 -05:00
missingCase := metav1 . ObjectMeta {
2016-02-12 17:38:22 -05:00
Name : "123" ,
Namespace : "ns" ,
Labels : map [ string ] string {
"other" : "blah" ,
} ,
}
if errs := ValidateHasLabel ( missingCase , field . NewPath ( "field" ) , "foo" , "bar" ) ; len ( errs ) == 0 {
t . Errorf ( "expected failure" )
}
2017-01-16 22:38:19 -05:00
wrongValueCase := metav1 . ObjectMeta {
2016-02-12 17:38:22 -05:00
Name : "123" ,
Namespace : "ns" ,
Labels : map [ string ] string {
"other" : "blah" ,
"foo" : "notbar" ,
} ,
}
if errs := ValidateHasLabel ( wrongValueCase , field . NewPath ( "field" ) , "foo" , "bar" ) ; len ( errs ) == 0 {
t . Errorf ( "expected failure" )
}
}
2016-06-14 09:09:53 -04:00
func TestIsValidSysctlName ( t * testing . T ) {
valid := [ ] string {
"a.b.c.d" ,
"a" ,
"a_b" ,
"a-b" ,
"abc" ,
"abc.def" ,
}
invalid := [ ] string {
"" ,
"*" ,
"ä" ,
"a_" ,
"_" ,
"__" ,
"_a" ,
"_a._b" ,
"-" ,
"." ,
"a." ,
".a" ,
"a.b." ,
"a*.b" ,
"a*b" ,
"*a" ,
"a.*" ,
"*" ,
"abc*" ,
"a.abc*" ,
"a.b.*" ,
"Abc" ,
func ( n int ) string {
x := make ( [ ] byte , n )
for i := range x {
x [ i ] = byte ( 'a' )
}
return string ( x )
} ( 256 ) ,
}
for _ , s := range valid {
if ! IsValidSysctlName ( s ) {
t . Errorf ( "%q expected to be a valid sysctl name" , s )
}
}
for _ , s := range invalid {
if IsValidSysctlName ( s ) {
t . Errorf ( "%q expected to be an invalid sysctl name" , s )
}
}
}
func TestValidateSysctls ( t * testing . T ) {
valid := [ ] string {
"net.foo.bar" ,
"kernel.shmmax" ,
}
invalid := [ ] string {
"i..nvalid" ,
"_invalid" ,
}
sysctls := make ( [ ] api . Sysctl , len ( valid ) )
for i , sysctl := range valid {
sysctls [ i ] . Name = sysctl
}
errs := validateSysctls ( sysctls , field . NewPath ( "foo" ) )
if len ( errs ) != 0 {
t . Errorf ( "unexpected validation errors: %v" , errs )
}
sysctls = make ( [ ] api . Sysctl , len ( invalid ) )
for i , sysctl := range invalid {
sysctls [ i ] . Name = sysctl
}
errs = validateSysctls ( sysctls , field . NewPath ( "foo" ) )
if len ( errs ) != 2 {
t . Errorf ( "expected 2 validation errors. Got: %v" , errs )
} else {
if got , expected := errs [ 0 ] . Error ( ) , "foo" ; ! strings . Contains ( got , expected ) {
t . Errorf ( "unexpected errors: expected=%q, got=%q" , expected , got )
}
if got , expected := errs [ 1 ] . Error ( ) , "foo" ; ! strings . Contains ( got , expected ) {
t . Errorf ( "unexpected errors: expected=%q, got=%q" , expected , got )
}
}
}
2016-08-24 12:57:38 -04:00
func newNodeNameEndpoint ( nodeName string ) * api . Endpoints {
ep := & api . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-08-24 12:57:38 -04:00
Name : "foo" ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2016-08-24 12:57:38 -04:00
ResourceVersion : "1" ,
} ,
Subsets : [ ] api . EndpointSubset {
{
NotReadyAddresses : [ ] api . EndpointAddress { } ,
Ports : [ ] api . EndpointPort { { Name : "https" , Port : 443 , Protocol : "TCP" } } ,
Addresses : [ ] api . EndpointAddress {
{
IP : "8.8.8.8" ,
Hostname : "zookeeper1" ,
NodeName : & nodeName } } } } }
return ep
}
func TestEndpointAddressNodeNameUpdateRestrictions ( t * testing . T ) {
2016-11-22 01:48:06 -05:00
oldEndpoint := newNodeNameEndpoint ( "kubernetes-node-setup-by-backend" )
2016-08-24 12:57:38 -04:00
updatedEndpoint := newNodeNameEndpoint ( "kubernetes-changed-nodename" )
// Check that NodeName cannot be changed during update (if already set)
errList := ValidateEndpoints ( updatedEndpoint )
errList = append ( errList , ValidateEndpointsUpdate ( updatedEndpoint , oldEndpoint ) ... )
if len ( errList ) == 0 {
t . Error ( "Endpoint should not allow changing of Subset.Addresses.NodeName on update" )
}
}
2016-09-04 19:23:03 -04:00
func TestEndpointAddressNodeNameInvalidDNSSubdomain ( t * testing . T ) {
2016-08-24 12:57:38 -04:00
// Check NodeName DNS validation
2016-09-04 19:23:03 -04:00
endpoint := newNodeNameEndpoint ( "illegal*.nodename" )
2016-08-24 12:57:38 -04:00
errList := ValidateEndpoints ( endpoint )
if len ( errList ) == 0 {
t . Error ( "Endpoint should reject invalid NodeName" )
}
}
2016-09-04 19:23:03 -04:00
func TestEndpointAddressNodeNameCanBeAnIPAddress ( t * testing . T ) {
endpoint := newNodeNameEndpoint ( "10.10.1.1" )
errList := ValidateEndpoints ( endpoint )
if len ( errList ) != 0 {
t . Error ( "Endpoint should accept a NodeName that is an IP address" )
}
}
2017-01-05 15:45:59 -05:00
func TestValidateFlexVolumeSource ( t * testing . T ) {
testcases := map [ string ] struct {
source * api . FlexVolumeSource
expectedErrs map [ string ] string
} {
"valid" : {
source : & api . FlexVolumeSource { Driver : "foo" } ,
expectedErrs : map [ string ] string { } ,
} ,
"valid with options" : {
source : & api . FlexVolumeSource { Driver : "foo" , Options : map [ string ] string { "foo" : "bar" } } ,
expectedErrs : map [ string ] string { } ,
} ,
"no driver" : {
source : & api . FlexVolumeSource { Driver : "" } ,
expectedErrs : map [ string ] string { "driver" : "Required value" } ,
} ,
"reserved option keys" : {
source : & api . FlexVolumeSource {
Driver : "foo" ,
Options : map [ string ] string {
// valid options
"myns.io" : "A" ,
"myns.io/bar" : "A" ,
"myns.io/kubernetes.io" : "A" ,
// invalid options
"KUBERNETES.IO" : "A" ,
"kubernetes.io" : "A" ,
"kubernetes.io/" : "A" ,
"kubernetes.io/foo" : "A" ,
"alpha.kubernetes.io" : "A" ,
"alpha.kubernetes.io/" : "A" ,
"alpha.kubernetes.io/foo" : "A" ,
"k8s.io" : "A" ,
"k8s.io/" : "A" ,
"k8s.io/foo" : "A" ,
"alpha.k8s.io" : "A" ,
"alpha.k8s.io/" : "A" ,
"alpha.k8s.io/foo" : "A" ,
} ,
} ,
expectedErrs : map [ string ] string {
"options[KUBERNETES.IO]" : "reserved" ,
"options[kubernetes.io]" : "reserved" ,
"options[kubernetes.io/]" : "reserved" ,
"options[kubernetes.io/foo]" : "reserved" ,
"options[alpha.kubernetes.io]" : "reserved" ,
"options[alpha.kubernetes.io/]" : "reserved" ,
"options[alpha.kubernetes.io/foo]" : "reserved" ,
"options[k8s.io]" : "reserved" ,
"options[k8s.io/]" : "reserved" ,
"options[k8s.io/foo]" : "reserved" ,
"options[alpha.k8s.io]" : "reserved" ,
"options[alpha.k8s.io/]" : "reserved" ,
"options[alpha.k8s.io/foo]" : "reserved" ,
} ,
} ,
}
for k , tc := range testcases {
errs := validateFlexVolumeSource ( tc . source , nil )
for _ , err := range errs {
expectedErr , ok := tc . expectedErrs [ err . Field ]
if ! ok {
t . Errorf ( "%s: unexpected err on field %s: %v" , k , err . Field , err )
continue
}
if ! strings . Contains ( err . Error ( ) , expectedErr ) {
t . Errorf ( "%s: expected err on field %s to contain '%s', was %v" , k , err . Field , expectedErr , err . Error ( ) )
continue
}
}
if len ( errs ) != len ( tc . expectedErrs ) {
t . Errorf ( "%s: expected errs %#v, got %#v" , k , tc . expectedErrs , errs )
continue
}
}
}
2017-08-01 12:09:37 -04:00
func TestValidateOrSetClientIPAffinityConfig ( t * testing . T ) {
successCases := map [ string ] * api . SessionAffinityConfig {
"non-empty config, valid timeout: 1" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( 1 ) ,
} ,
} ,
"non-empty config, valid timeout: api.MaxClientIPServiceAffinitySeconds-1" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( int ( api . MaxClientIPServiceAffinitySeconds - 1 ) ) ,
} ,
} ,
"non-empty config, valid timeout: api.MaxClientIPServiceAffinitySeconds" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( int ( api . MaxClientIPServiceAffinitySeconds ) ) ,
} ,
} ,
}
for name , test := range successCases {
if errs := validateClientIPAffinityConfig ( test , field . NewPath ( "field" ) ) ; len ( errs ) != 0 {
t . Errorf ( "case: %s, expected success: %v" , name , errs )
}
}
errorCases := map [ string ] * api . SessionAffinityConfig {
"empty session affinity config" : nil ,
"empty client IP config" : {
ClientIP : nil ,
} ,
"empty timeoutSeconds" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : nil ,
} ,
} ,
"non-empty config, invalid timeout: api.MaxClientIPServiceAffinitySeconds+1" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( int ( api . MaxClientIPServiceAffinitySeconds + 1 ) ) ,
} ,
} ,
"non-empty config, invalid timeout: -1" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( - 1 ) ,
} ,
} ,
"non-empty config, invalid timeout: 0" : {
ClientIP : & api . ClientIPConfig {
TimeoutSeconds : newInt32 ( 0 ) ,
} ,
} ,
}
for name , test := range errorCases {
if errs := validateClientIPAffinityConfig ( test , field . NewPath ( "field" ) ) ; len ( errs ) == 0 {
t . Errorf ( "case: %v, expected failures: %v" , name , errs )
}
}
}