Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
92f373a4
Commit
92f373a4
authored
Aug 25, 2015
by
nikhiljindal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Validate deployment
parent
2321d9ee
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
369 additions
and
133 deletions
+369
-133
validation.go
pkg/api/validation/validation.go
+52
-24
validation_test.go
pkg/api/validation/validation_test.go
+0
-5
deep_copy_generated.go
pkg/expapi/deep_copy_generated.go
+1
-6
types.go
pkg/expapi/types.go
+2
-3
conversion.go
pkg/expapi/v1/conversion.go
+87
-0
conversion_generated.go
pkg/expapi/v1/conversion_generated.go
+0
-95
validation.go
pkg/expapi/validation/validation.go
+101
-0
validation_test.go
pkg/expapi/validation/validation_test.go
+118
-0
validation.go
pkg/util/validation.go
+8
-0
No files found.
pkg/api/validation/validation.go
View file @
92f373a4
...
@@ -53,13 +53,19 @@ var portNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15
...
@@ -53,13 +53,19 @@ var portNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15
const
totalAnnotationSizeLimitB
int
=
64
*
(
1
<<
10
)
// 64 kB
const
totalAnnotationSizeLimitB
int
=
64
*
(
1
<<
10
)
// 64 kB
func
ValidateLabelName
(
labelName
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
if
!
util
.
IsQualifiedName
(
labelName
)
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
,
labelName
,
qualifiedNameErrorMsg
))
}
return
allErrs
}
// ValidateLabels validates that a set of labels are correctly defined.
// ValidateLabels validates that a set of labels are correctly defined.
func
ValidateLabels
(
labels
map
[
string
]
string
,
field
string
)
errs
.
ValidationErrorList
{
func
ValidateLabels
(
labels
map
[
string
]
string
,
field
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
:=
errs
.
ValidationErrorList
{}
for
k
,
v
:=
range
labels
{
for
k
,
v
:=
range
labels
{
if
!
util
.
IsQualifiedName
(
k
)
{
allErrs
=
append
(
allErrs
,
ValidateLabelName
(
k
,
field
)
...
)
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
field
,
k
,
qualifiedNameErrorMsg
))
}
if
!
util
.
IsValidLabelValue
(
v
)
{
if
!
util
.
IsValidLabelValue
(
v
)
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
field
,
v
,
labelValueErrorMsg
))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
field
,
v
,
labelValueErrorMsg
))
}
}
...
@@ -202,6 +208,15 @@ func NameIsDNS952Label(name string, prefix bool) (bool, string) {
...
@@ -202,6 +208,15 @@ func NameIsDNS952Label(name string, prefix bool) (bool, string) {
return
false
,
DNS952LabelErrorMsg
return
false
,
DNS952LabelErrorMsg
}
}
// Validates that given value is not negative.
func
ValidatePositiveField
(
value
int64
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
if
value
<
0
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
,
value
,
isNegativeErrorMsg
))
}
return
allErrs
}
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
// been performed.
func
ValidateObjectMeta
(
meta
*
api
.
ObjectMeta
,
requiresNamespace
bool
,
nameFn
ValidateNameFunc
)
errs
.
ValidationErrorList
{
func
ValidateObjectMeta
(
meta
*
api
.
ObjectMeta
,
requiresNamespace
bool
,
nameFn
ValidateNameFunc
)
errs
.
ValidationErrorList
{
...
@@ -224,9 +239,7 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
...
@@ -224,9 +239,7 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
"name"
,
meta
.
Name
,
qualifier
))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
"name"
,
meta
.
Name
,
qualifier
))
}
}
}
}
if
meta
.
Generation
<
0
{
allErrs
=
append
(
allErrs
,
ValidatePositiveField
(
meta
.
Generation
,
"generation"
)
...
)
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
"generation"
,
meta
.
Generation
,
isNegativeErrorMsg
))
}
if
requiresNamespace
{
if
requiresNamespace
{
if
len
(
meta
.
Namespace
)
==
0
{
if
len
(
meta
.
Namespace
)
==
0
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
"namespace"
))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
"namespace"
))
...
@@ -1206,37 +1219,52 @@ func ValidateReplicationControllerUpdate(oldController, controller *api.Replicat
...
@@ -1206,37 +1219,52 @@ func ValidateReplicationControllerUpdate(oldController, controller *api.Replicat
return
allErrs
return
allErrs
}
}
// Validate
ReplicationControllerSpec tests if required fields in the replication controller spec are set
.
// Validate
s that the given selector is non-empty
.
func
Validate
ReplicationControllerSpec
(
spec
*
api
.
ReplicationControllerSpec
)
errs
.
ValidationErrorList
{
func
Validate
NonEmptySelector
(
selectorMap
map
[
string
]
string
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
:=
errs
.
ValidationErrorList
{}
selector
:=
labels
.
Set
(
selectorMap
)
.
AsSelector
()
selector
:=
labels
.
Set
(
spec
.
Selector
)
.
AsSelector
()
if
selector
.
Empty
()
{
if
selector
.
Empty
()
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
"selector"
))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
fieldName
))
}
if
spec
.
Replicas
<
0
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
"replicas"
,
spec
.
Replicas
,
isNegativeErrorMsg
))
}
}
return
allErrs
}
if
spec
.
Template
==
nil
{
// Validates the given template and ensures that it is in accordance with the desrired selector and replicas.
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
"template"
))
func
ValidatePodTemplateSpecForRC
(
template
*
api
.
PodTemplateSpec
,
selectorMap
map
[
string
]
string
,
replicas
int
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
if
template
==
nil
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldRequired
(
fieldName
))
}
else
{
}
else
{
labels
:=
labels
.
Set
(
spec
.
Template
.
Labels
)
selector
:=
labels
.
Set
(
selectorMap
)
.
AsSelector
()
if
!
selector
.
Matches
(
labels
)
{
if
!
selector
.
Empty
()
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
"template.metadata.labels"
,
spec
.
Template
.
Labels
,
"selector does not match template"
))
// Verify that the RC selector matches the labels in template.
labels
:=
labels
.
Set
(
template
.
Labels
)
if
!
selector
.
Matches
(
labels
)
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
+
".metadata.labels"
,
template
.
Labels
,
"selector does not match labels in "
+
fieldName
))
}
}
}
allErrs
=
append
(
allErrs
,
ValidatePodTemplateSpec
(
spec
.
Template
)
.
Prefix
(
"template"
)
...
)
allErrs
=
append
(
allErrs
,
ValidatePodTemplateSpec
(
template
)
.
Prefix
(
fieldName
)
...
)
if
spec
.
R
eplicas
>
1
{
if
r
eplicas
>
1
{
allErrs
=
append
(
allErrs
,
ValidateReadOnlyPersistentDisks
(
spec
.
Template
.
Spec
.
Volumes
)
.
Prefix
(
"template
.spec.volumes"
)
...
)
allErrs
=
append
(
allErrs
,
ValidateReadOnlyPersistentDisks
(
template
.
Spec
.
Volumes
)
.
Prefix
(
fieldName
+
"
.spec.volumes"
)
...
)
}
}
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
if
spec
.
T
emplate
.
Spec
.
RestartPolicy
!=
api
.
RestartPolicyAlways
{
if
t
emplate
.
Spec
.
RestartPolicy
!=
api
.
RestartPolicyAlways
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldValueNotSupported
(
"template.spec.restartPolicy"
,
spec
.
T
emplate
.
Spec
.
RestartPolicy
,
[]
string
{
string
(
api
.
RestartPolicyAlways
)}))
allErrs
=
append
(
allErrs
,
errs
.
NewFieldValueNotSupported
(
fieldName
+
".spec.restartPolicy"
,
t
emplate
.
Spec
.
RestartPolicy
,
[]
string
{
string
(
api
.
RestartPolicyAlways
)}))
}
}
}
}
return
allErrs
return
allErrs
}
}
// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func
ValidateReplicationControllerSpec
(
spec
*
api
.
ReplicationControllerSpec
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
=
append
(
allErrs
,
ValidateNonEmptySelector
(
spec
.
Selector
,
"selector"
)
...
)
allErrs
=
append
(
allErrs
,
ValidatePositiveField
(
int64
(
spec
.
Replicas
),
"replicas"
)
...
)
allErrs
=
append
(
allErrs
,
ValidatePodTemplateSpecForRC
(
spec
.
Template
,
spec
.
Selector
,
spec
.
Replicas
,
"template"
)
...
)
return
allErrs
}
// ValidatePodTemplateSpec validates the spec of a pod template
// ValidatePodTemplateSpec validates the spec of a pod template
func
ValidatePodTemplateSpec
(
spec
*
api
.
PodTemplateSpec
)
errs
.
ValidationErrorList
{
func
ValidatePodTemplateSpec
(
spec
*
api
.
PodTemplateSpec
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
:=
errs
.
ValidationErrorList
{}
...
...
pkg/api/validation/validation_test.go
View file @
92f373a4
...
@@ -234,7 +234,6 @@ func TestValidateAnnotations(t *testing.T) {
...
@@ -234,7 +234,6 @@ func TestValidateAnnotations(t *testing.T) {
}
}
func
testVolume
(
name
string
,
namespace
string
,
spec
api
.
PersistentVolumeSpec
)
*
api
.
PersistentVolume
{
func
testVolume
(
name
string
,
namespace
string
,
spec
api
.
PersistentVolumeSpec
)
*
api
.
PersistentVolume
{
objMeta
:=
api
.
ObjectMeta
{
Name
:
name
}
objMeta
:=
api
.
ObjectMeta
{
Name
:
name
}
if
namespace
!=
""
{
if
namespace
!=
""
{
objMeta
.
Namespace
=
namespace
objMeta
.
Namespace
=
namespace
...
@@ -247,7 +246,6 @@ func testVolume(name string, namespace string, spec api.PersistentVolumeSpec) *a
...
@@ -247,7 +246,6 @@ func testVolume(name string, namespace string, spec api.PersistentVolumeSpec) *a
}
}
func
TestValidatePersistentVolumes
(
t
*
testing
.
T
)
{
func
TestValidatePersistentVolumes
(
t
*
testing
.
T
)
{
scenarios
:=
map
[
string
]
struct
{
scenarios
:=
map
[
string
]
struct
{
isExpectedFailure
bool
isExpectedFailure
bool
volume
*
api
.
PersistentVolume
volume
*
api
.
PersistentVolume
...
@@ -358,7 +356,6 @@ func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeCla
...
@@ -358,7 +356,6 @@ func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeCla
}
}
func
TestValidatePersistentVolumeClaim
(
t
*
testing
.
T
)
{
func
TestValidatePersistentVolumeClaim
(
t
*
testing
.
T
)
{
scenarios
:=
map
[
string
]
struct
{
scenarios
:=
map
[
string
]
struct
{
isExpectedFailure
bool
isExpectedFailure
bool
claim
*
api
.
PersistentVolumeClaim
claim
*
api
.
PersistentVolumeClaim
...
@@ -802,7 +799,6 @@ func TestValidatePullPolicy(t *testing.T) {
...
@@ -802,7 +799,6 @@ func TestValidatePullPolicy(t *testing.T) {
t
.
Errorf
(
"case[%s] expected policy %v, got %v"
,
k
,
v
.
ExpectedPolicy
,
ctr
.
ImagePullPolicy
)
t
.
Errorf
(
"case[%s] expected policy %v, got %v"
,
k
,
v
.
ExpectedPolicy
,
ctr
.
ImagePullPolicy
)
}
}
}
}
}
}
func
getResourceLimits
(
cpu
,
memory
string
)
api
.
ResourceList
{
func
getResourceLimits
(
cpu
,
memory
string
)
api
.
ResourceList
{
...
@@ -2144,7 +2140,6 @@ func TestValidateReplicationControllerUpdate(t *testing.T) {
...
@@ -2144,7 +2140,6 @@ func TestValidateReplicationControllerUpdate(t *testing.T) {
t
.
Errorf
(
"expected failure: %s"
,
testName
)
t
.
Errorf
(
"expected failure: %s"
,
testName
)
}
}
}
}
}
}
func
TestValidateReplicationController
(
t
*
testing
.
T
)
{
func
TestValidateReplicationController
(
t
*
testing
.
T
)
{
...
...
pkg/expapi/deep_copy_generated.go
View file @
92f373a4
...
@@ -787,12 +787,7 @@ func deepCopy_expapi_DeploymentList(in DeploymentList, out *DeploymentList, c *c
...
@@ -787,12 +787,7 @@ func deepCopy_expapi_DeploymentList(in DeploymentList, out *DeploymentList, c *c
}
}
func
deepCopy_expapi_DeploymentSpec
(
in
DeploymentSpec
,
out
*
DeploymentSpec
,
c
*
conversion
.
Cloner
)
error
{
func
deepCopy_expapi_DeploymentSpec
(
in
DeploymentSpec
,
out
*
DeploymentSpec
,
c
*
conversion
.
Cloner
)
error
{
if
in
.
Replicas
!=
nil
{
out
.
Replicas
=
in
.
Replicas
out
.
Replicas
=
new
(
int
)
*
out
.
Replicas
=
*
in
.
Replicas
}
else
{
out
.
Replicas
=
nil
}
if
in
.
Selector
!=
nil
{
if
in
.
Selector
!=
nil
{
out
.
Selector
=
make
(
map
[
string
]
string
)
out
.
Selector
=
make
(
map
[
string
]
string
)
for
key
,
val
:=
range
in
.
Selector
{
for
key
,
val
:=
range
in
.
Selector
{
...
...
pkg/expapi/types.go
View file @
92f373a4
...
@@ -179,9 +179,8 @@ type Deployment struct {
...
@@ -179,9 +179,8 @@ type Deployment struct {
}
}
type
DeploymentSpec
struct
{
type
DeploymentSpec
struct
{
// Number of desired pods. This is a pointer to distinguish between explicit
// Number of desired pods.
// zero and not specified. Defaults to 1.
Replicas
int
`json:"replicas,omitempty"`
Replicas
*
int
`json:"replicas,omitempty"`
// Label selector for pods. Existing ReplicationControllers whose pods are
// Label selector for pods. Existing ReplicationControllers whose pods are
// selected by this will be scaled down.
// selected by this will be scaled down.
...
...
pkg/expapi/v1/conversion.go
View file @
92f373a4
...
@@ -22,6 +22,7 @@ import (
...
@@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
v1
"k8s.io/kubernetes/pkg/api/v1"
v1
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/expapi"
)
)
func
addConversionFuncs
()
{
func
addConversionFuncs
()
{
...
@@ -29,6 +30,9 @@ func addConversionFuncs() {
...
@@ -29,6 +30,9 @@ func addConversionFuncs() {
err
:=
api
.
Scheme
.
AddConversionFuncs
(
err
:=
api
.
Scheme
.
AddConversionFuncs
(
convert_api_PodSpec_To_v1_PodSpec
,
convert_api_PodSpec_To_v1_PodSpec
,
convert_v1_PodSpec_To_api_PodSpec
,
convert_v1_PodSpec_To_api_PodSpec
,
convert_expapi_DeploymentSpec_To_v1_DeploymentSpec
,
convert_v1_DeploymentSpec_To_expapi_DeploymentSpec
,
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
,
)
)
if
err
!=
nil
{
if
err
!=
nil
{
// If one of the conversion functions is malformed, detect it immediately.
// If one of the conversion functions is malformed, detect it immediately.
...
@@ -169,3 +173,86 @@ func convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conve
...
@@ -169,3 +173,86 @@ func convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conve
}
}
return
nil
return
nil
}
}
func
convert_expapi_DeploymentSpec_To_v1_DeploymentSpec
(
in
*
expapi
.
DeploymentSpec
,
out
*
DeploymentSpec
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
expapi
.
DeploymentSpec
))(
in
)
}
out
.
Replicas
=
new
(
int
)
*
out
.
Replicas
=
in
.
Replicas
if
in
.
Selector
!=
nil
{
out
.
Selector
=
make
(
map
[
string
]
string
)
for
key
,
val
:=
range
in
.
Selector
{
out
.
Selector
[
key
]
=
val
}
}
else
{
out
.
Selector
=
nil
}
if
in
.
Template
!=
nil
{
out
.
Template
=
new
(
v1
.
PodTemplateSpec
)
if
err
:=
convert_api_PodTemplateSpec_To_v1_PodTemplateSpec
(
in
.
Template
,
out
.
Template
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
Template
=
nil
}
if
err
:=
convert_expapi_DeploymentStrategy_To_v1_DeploymentStrategy
(
&
in
.
Strategy
,
&
out
.
Strategy
,
s
);
err
!=
nil
{
return
err
}
if
in
.
UniqueLabelKey
!=
nil
{
out
.
UniqueLabelKey
=
new
(
string
)
*
out
.
UniqueLabelKey
=
*
in
.
UniqueLabelKey
}
else
{
out
.
UniqueLabelKey
=
nil
}
return
nil
}
func
convert_v1_DeploymentSpec_To_expapi_DeploymentSpec
(
in
*
DeploymentSpec
,
out
*
expapi
.
DeploymentSpec
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
DeploymentSpec
))(
in
)
}
out
.
Replicas
=
*
in
.
Replicas
if
in
.
Selector
!=
nil
{
out
.
Selector
=
make
(
map
[
string
]
string
)
for
key
,
val
:=
range
in
.
Selector
{
out
.
Selector
[
key
]
=
val
}
}
else
{
out
.
Selector
=
nil
}
if
in
.
Template
!=
nil
{
out
.
Template
=
new
(
api
.
PodTemplateSpec
)
if
err
:=
convert_v1_PodTemplateSpec_To_api_PodTemplateSpec
(
in
.
Template
,
out
.
Template
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
Template
=
nil
}
if
err
:=
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
(
&
in
.
Strategy
,
&
out
.
Strategy
,
s
);
err
!=
nil
{
return
err
}
if
in
.
UniqueLabelKey
!=
nil
{
out
.
UniqueLabelKey
=
new
(
string
)
*
out
.
UniqueLabelKey
=
*
in
.
UniqueLabelKey
}
else
{
out
.
UniqueLabelKey
=
nil
}
return
nil
}
func
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
(
in
*
DeploymentStrategy
,
out
*
expapi
.
DeploymentStrategy
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
DeploymentStrategy
))(
in
)
}
out
.
Type
=
expapi
.
DeploymentType
(
in
.
Type
)
if
in
.
RollingUpdate
!=
nil
{
out
.
RollingUpdate
=
new
(
expapi
.
RollingUpdateDeployment
)
if
err
:=
convert_v1_RollingUpdateDeployment_To_expapi_RollingUpdateDeployment
(
in
.
RollingUpdate
,
out
.
RollingUpdate
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
RollingUpdate
=
nil
}
return
nil
}
pkg/expapi/v1/conversion_generated.go
View file @
92f373a4
...
@@ -1512,44 +1512,6 @@ func convert_expapi_DeploymentList_To_v1_DeploymentList(in *expapi.DeploymentLis
...
@@ -1512,44 +1512,6 @@ func convert_expapi_DeploymentList_To_v1_DeploymentList(in *expapi.DeploymentLis
return
nil
return
nil
}
}
func
convert_expapi_DeploymentSpec_To_v1_DeploymentSpec
(
in
*
expapi
.
DeploymentSpec
,
out
*
DeploymentSpec
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
expapi
.
DeploymentSpec
))(
in
)
}
if
in
.
Replicas
!=
nil
{
out
.
Replicas
=
new
(
int
)
*
out
.
Replicas
=
*
in
.
Replicas
}
else
{
out
.
Replicas
=
nil
}
if
in
.
Selector
!=
nil
{
out
.
Selector
=
make
(
map
[
string
]
string
)
for
key
,
val
:=
range
in
.
Selector
{
out
.
Selector
[
key
]
=
val
}
}
else
{
out
.
Selector
=
nil
}
if
in
.
Template
!=
nil
{
out
.
Template
=
new
(
v1
.
PodTemplateSpec
)
if
err
:=
convert_api_PodTemplateSpec_To_v1_PodTemplateSpec
(
in
.
Template
,
out
.
Template
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
Template
=
nil
}
if
err
:=
convert_expapi_DeploymentStrategy_To_v1_DeploymentStrategy
(
&
in
.
Strategy
,
&
out
.
Strategy
,
s
);
err
!=
nil
{
return
err
}
if
in
.
UniqueLabelKey
!=
nil
{
out
.
UniqueLabelKey
=
new
(
string
)
*
out
.
UniqueLabelKey
=
*
in
.
UniqueLabelKey
}
else
{
out
.
UniqueLabelKey
=
nil
}
return
nil
}
func
convert_expapi_DeploymentStatus_To_v1_DeploymentStatus
(
in
*
expapi
.
DeploymentStatus
,
out
*
DeploymentStatus
,
s
conversion
.
Scope
)
error
{
func
convert_expapi_DeploymentStatus_To_v1_DeploymentStatus
(
in
*
expapi
.
DeploymentStatus
,
out
*
DeploymentStatus
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
expapi
.
DeploymentStatus
))(
in
)
defaulting
.
(
func
(
*
expapi
.
DeploymentStatus
))(
in
)
...
@@ -1929,44 +1891,6 @@ func convert_v1_DeploymentList_To_expapi_DeploymentList(in *DeploymentList, out
...
@@ -1929,44 +1891,6 @@ func convert_v1_DeploymentList_To_expapi_DeploymentList(in *DeploymentList, out
return
nil
return
nil
}
}
func
convert_v1_DeploymentSpec_To_expapi_DeploymentSpec
(
in
*
DeploymentSpec
,
out
*
expapi
.
DeploymentSpec
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
DeploymentSpec
))(
in
)
}
if
in
.
Replicas
!=
nil
{
out
.
Replicas
=
new
(
int
)
*
out
.
Replicas
=
*
in
.
Replicas
}
else
{
out
.
Replicas
=
nil
}
if
in
.
Selector
!=
nil
{
out
.
Selector
=
make
(
map
[
string
]
string
)
for
key
,
val
:=
range
in
.
Selector
{
out
.
Selector
[
key
]
=
val
}
}
else
{
out
.
Selector
=
nil
}
if
in
.
Template
!=
nil
{
out
.
Template
=
new
(
api
.
PodTemplateSpec
)
if
err
:=
convert_v1_PodTemplateSpec_To_api_PodTemplateSpec
(
in
.
Template
,
out
.
Template
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
Template
=
nil
}
if
err
:=
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
(
&
in
.
Strategy
,
&
out
.
Strategy
,
s
);
err
!=
nil
{
return
err
}
if
in
.
UniqueLabelKey
!=
nil
{
out
.
UniqueLabelKey
=
new
(
string
)
*
out
.
UniqueLabelKey
=
*
in
.
UniqueLabelKey
}
else
{
out
.
UniqueLabelKey
=
nil
}
return
nil
}
func
convert_v1_DeploymentStatus_To_expapi_DeploymentStatus
(
in
*
DeploymentStatus
,
out
*
expapi
.
DeploymentStatus
,
s
conversion
.
Scope
)
error
{
func
convert_v1_DeploymentStatus_To_expapi_DeploymentStatus
(
in
*
DeploymentStatus
,
out
*
expapi
.
DeploymentStatus
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
DeploymentStatus
))(
in
)
defaulting
.
(
func
(
*
DeploymentStatus
))(
in
)
...
@@ -1976,22 +1900,6 @@ func convert_v1_DeploymentStatus_To_expapi_DeploymentStatus(in *DeploymentStatus
...
@@ -1976,22 +1900,6 @@ func convert_v1_DeploymentStatus_To_expapi_DeploymentStatus(in *DeploymentStatus
return
nil
return
nil
}
}
func
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
(
in
*
DeploymentStrategy
,
out
*
expapi
.
DeploymentStrategy
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
DeploymentStrategy
))(
in
)
}
out
.
Type
=
expapi
.
DeploymentType
(
in
.
Type
)
if
in
.
RollingUpdate
!=
nil
{
out
.
RollingUpdate
=
new
(
expapi
.
RollingUpdateDeployment
)
if
err
:=
convert_v1_RollingUpdateDeployment_To_expapi_RollingUpdateDeployment
(
in
.
RollingUpdate
,
out
.
RollingUpdate
,
s
);
err
!=
nil
{
return
err
}
}
else
{
out
.
RollingUpdate
=
nil
}
return
nil
}
func
convert_v1_HorizontalPodAutoscaler_To_expapi_HorizontalPodAutoscaler
(
in
*
HorizontalPodAutoscaler
,
out
*
expapi
.
HorizontalPodAutoscaler
,
s
conversion
.
Scope
)
error
{
func
convert_v1_HorizontalPodAutoscaler_To_expapi_HorizontalPodAutoscaler
(
in
*
HorizontalPodAutoscaler
,
out
*
expapi
.
HorizontalPodAutoscaler
,
s
conversion
.
Scope
)
error
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
if
defaulting
,
found
:=
s
.
DefaultingInterface
(
reflect
.
TypeOf
(
*
in
));
found
{
defaulting
.
(
func
(
*
HorizontalPodAutoscaler
))(
in
)
defaulting
.
(
func
(
*
HorizontalPodAutoscaler
))(
in
)
...
@@ -2262,7 +2170,6 @@ func init() {
...
@@ -2262,7 +2170,6 @@ func init() {
convert_expapi_DaemonStatus_To_v1_DaemonStatus
,
convert_expapi_DaemonStatus_To_v1_DaemonStatus
,
convert_expapi_Daemon_To_v1_Daemon
,
convert_expapi_Daemon_To_v1_Daemon
,
convert_expapi_DeploymentList_To_v1_DeploymentList
,
convert_expapi_DeploymentList_To_v1_DeploymentList
,
convert_expapi_DeploymentSpec_To_v1_DeploymentSpec
,
convert_expapi_DeploymentStatus_To_v1_DeploymentStatus
,
convert_expapi_DeploymentStatus_To_v1_DeploymentStatus
,
convert_expapi_DeploymentStrategy_To_v1_DeploymentStrategy
,
convert_expapi_DeploymentStrategy_To_v1_DeploymentStrategy
,
convert_expapi_Deployment_To_v1_Deployment
,
convert_expapi_Deployment_To_v1_Deployment
,
...
@@ -2289,9 +2196,7 @@ func init() {
...
@@ -2289,9 +2196,7 @@ func init() {
convert_v1_DaemonStatus_To_expapi_DaemonStatus
,
convert_v1_DaemonStatus_To_expapi_DaemonStatus
,
convert_v1_Daemon_To_expapi_Daemon
,
convert_v1_Daemon_To_expapi_Daemon
,
convert_v1_DeploymentList_To_expapi_DeploymentList
,
convert_v1_DeploymentList_To_expapi_DeploymentList
,
convert_v1_DeploymentSpec_To_expapi_DeploymentSpec
,
convert_v1_DeploymentStatus_To_expapi_DeploymentStatus
,
convert_v1_DeploymentStatus_To_expapi_DeploymentStatus
,
convert_v1_DeploymentStrategy_To_expapi_DeploymentStrategy
,
convert_v1_Deployment_To_expapi_Deployment
,
convert_v1_Deployment_To_expapi_Deployment
,
convert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource
,
convert_v1_EmptyDirVolumeSource_To_api_EmptyDirVolumeSource
,
convert_v1_EnvVarSource_To_api_EnvVarSource
,
convert_v1_EnvVarSource_To_api_EnvVarSource
,
...
...
pkg/expapi/validation/validation.go
View file @
92f373a4
...
@@ -17,6 +17,8 @@ limitations under the License.
...
@@ -17,6 +17,8 @@ limitations under the License.
package
validation
package
validation
import
(
import
(
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
apivalidation
"k8s.io/kubernetes/pkg/api/validation"
apivalidation
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/expapi"
...
@@ -156,3 +158,102 @@ func ValidateDaemonSpec(spec *expapi.DaemonSpec) errs.ValidationErrorList {
...
@@ -156,3 +158,102 @@ func ValidateDaemonSpec(spec *expapi.DaemonSpec) errs.ValidationErrorList {
func
ValidateDaemonName
(
name
string
,
prefix
bool
)
(
bool
,
string
)
{
func
ValidateDaemonName
(
name
string
,
prefix
bool
)
(
bool
,
string
)
{
return
apivalidation
.
NameIsDNSSubdomain
(
name
,
prefix
)
return
apivalidation
.
NameIsDNSSubdomain
(
name
,
prefix
)
}
}
// Validates that the given name can be used as a deployment name.
func
ValidateDeploymentName
(
name
string
,
prefix
bool
)
(
bool
,
string
)
{
return
apivalidation
.
NameIsDNSSubdomain
(
name
,
prefix
)
}
func
ValidatePositiveIntOrPercent
(
intOrPercent
util
.
IntOrString
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
if
intOrPercent
.
Kind
==
util
.
IntstrString
{
if
!
util
.
IsValidPercent
(
intOrPercent
.
StrVal
)
{
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
,
intOrPercent
,
"value should be int(5) or percentage(5%)"
))
}
}
else
if
intOrPercent
.
Kind
==
util
.
IntstrInt
{
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidatePositiveField
(
int64
(
intOrPercent
.
IntVal
),
fieldName
)
...
)
}
return
allErrs
}
func
getPercentValue
(
intOrStringValue
util
.
IntOrString
)
(
int
,
bool
)
{
if
intOrStringValue
.
Kind
!=
util
.
IntstrString
||
!
util
.
IsValidPercent
(
intOrStringValue
.
StrVal
)
{
return
0
,
false
}
value
,
_
:=
strconv
.
Atoi
(
intOrStringValue
.
StrVal
[
:
len
(
intOrStringValue
.
StrVal
)
-
1
])
return
value
,
true
}
func
getIntOrPercentValue
(
intOrStringValue
util
.
IntOrString
)
int
{
value
,
isPercent
:=
getPercentValue
(
intOrStringValue
)
if
isPercent
{
return
value
}
return
intOrStringValue
.
IntVal
}
func
IsNotMoreThan100Percent
(
intOrStringValue
util
.
IntOrString
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
value
,
isPercent
:=
getPercentValue
(
intOrStringValue
)
if
!
isPercent
||
value
<=
100
{
return
nil
}
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
,
intOrStringValue
,
"should not be more than 100%"
))
return
allErrs
}
func
ValidateRollingUpdateDeployment
(
rollingUpdate
*
expapi
.
RollingUpdateDeployment
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
=
append
(
allErrs
,
ValidatePositiveIntOrPercent
(
rollingUpdate
.
MaxUnavailable
,
fieldName
+
"maxUnavailable"
)
...
)
allErrs
=
append
(
allErrs
,
ValidatePositiveIntOrPercent
(
rollingUpdate
.
MaxSurge
,
fieldName
+
".maxSurge"
)
...
)
if
getIntOrPercentValue
(
rollingUpdate
.
MaxUnavailable
)
==
0
&&
getIntOrPercentValue
(
rollingUpdate
.
MaxSurge
)
==
0
{
// Both MaxSurge and MaxUnavailable cannot be zero.
allErrs
=
append
(
allErrs
,
errs
.
NewFieldInvalid
(
fieldName
+
".maxUnavailable"
,
rollingUpdate
.
MaxUnavailable
,
"cannot be 0 when maxSurge is 0 as well"
))
}
// Validate that MaxUnavailable is not more than 100%.
allErrs
=
append
(
allErrs
,
IsNotMoreThan100Percent
(
rollingUpdate
.
MaxUnavailable
,
fieldName
+
".maxUnavailable"
)
...
)
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidatePositiveField
(
int64
(
rollingUpdate
.
MinReadySeconds
),
fieldName
+
".minReadySeconds"
)
...
)
return
allErrs
}
func
ValidateDeploymentStrategy
(
strategy
*
expapi
.
DeploymentStrategy
,
fieldName
string
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
if
strategy
.
RollingUpdate
==
nil
{
return
allErrs
}
switch
strategy
.
Type
{
case
expapi
.
DeploymentRecreate
:
allErrs
=
append
(
allErrs
,
errs
.
NewFieldForbidden
(
"rollingUpdate"
,
"rollingUpdate should be nil when strategy type is "
+
expapi
.
DeploymentRecreate
))
case
expapi
.
DeploymentRollingUpdate
:
allErrs
=
append
(
allErrs
,
ValidateRollingUpdateDeployment
(
strategy
.
RollingUpdate
,
"rollingUpdate"
)
...
)
}
return
allErrs
}
// Validates given deployment spec.
func
ValidateDeploymentSpec
(
spec
*
expapi
.
DeploymentSpec
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidateNonEmptySelector
(
spec
.
Selector
,
"selector"
)
...
)
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidatePositiveField
(
int64
(
spec
.
Replicas
),
"replicas"
)
...
)
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidatePodTemplateSpecForRC
(
spec
.
Template
,
spec
.
Selector
,
spec
.
Replicas
,
"template"
)
...
)
allErrs
=
append
(
allErrs
,
ValidateDeploymentStrategy
(
&
spec
.
Strategy
,
"strategy"
)
...
)
if
spec
.
UniqueLabelKey
!=
nil
{
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidateLabelName
(
*
spec
.
UniqueLabelKey
,
"uniqueLabel"
)
...
)
}
return
allErrs
}
func
ValidateDeploymentUpdate
(
old
,
update
*
expapi
.
Deployment
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidateObjectMetaUpdate
(
&
update
.
ObjectMeta
,
&
old
.
ObjectMeta
)
.
Prefix
(
"metadata"
)
...
)
allErrs
=
append
(
allErrs
,
ValidateDeploymentSpec
(
&
update
.
Spec
)
.
Prefix
(
"spec"
)
...
)
return
allErrs
}
func
ValidateDeployment
(
obj
*
expapi
.
Deployment
)
errs
.
ValidationErrorList
{
allErrs
:=
errs
.
ValidationErrorList
{}
allErrs
=
append
(
allErrs
,
apivalidation
.
ValidateObjectMeta
(
&
obj
.
ObjectMeta
,
true
,
ValidateDeploymentName
)
.
Prefix
(
"metadata"
)
...
)
allErrs
=
append
(
allErrs
,
ValidateDeploymentSpec
(
&
obj
.
Spec
)
.
Prefix
(
"spec"
)
...
)
return
allErrs
}
pkg/expapi/validation/validation_test.go
View file @
92f373a4
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/util"
errors
"k8s.io/kubernetes/pkg/util/fielderrors"
errors
"k8s.io/kubernetes/pkg/util/fielderrors"
)
)
...
@@ -540,3 +541,120 @@ func TestValidateDaemon(t *testing.T) {
...
@@ -540,3 +541,120 @@ func TestValidateDaemon(t *testing.T) {
}
}
}
}
}
}
func
validDeployment
()
*
expapi
.
Deployment
{
return
&
expapi
.
Deployment
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"abc"
,
Namespace
:
api
.
NamespaceDefault
,
},
Spec
:
expapi
.
DeploymentSpec
{
Selector
:
map
[
string
]
string
{
"name"
:
"abc"
,
},
Template
:
&
api
.
PodTemplateSpec
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"abc"
,
Namespace
:
api
.
NamespaceDefault
,
Labels
:
map
[
string
]
string
{
"name"
:
"abc"
,
},
},
Spec
:
api
.
PodSpec
{
RestartPolicy
:
api
.
RestartPolicyAlways
,
DNSPolicy
:
api
.
DNSDefault
,
Containers
:
[]
api
.
Container
{
{
Name
:
"nginx"
,
Image
:
"image"
,
ImagePullPolicy
:
api
.
PullNever
,
},
},
},
},
},
}
}
func
TestValidateDeployment
(
t
*
testing
.
T
)
{
successCases
:=
[]
*
expapi
.
Deployment
{
validDeployment
(),
}
for
_
,
successCase
:=
range
successCases
{
if
errs
:=
ValidateDeployment
(
successCase
);
len
(
errs
)
!=
0
{
t
.
Errorf
(
"expected success: %v"
,
errs
)
}
}
errorCases
:=
map
[
string
]
*
expapi
.
Deployment
{}
errorCases
[
"metadata.name: required value"
]
=
&
expapi
.
Deployment
{
ObjectMeta
:
api
.
ObjectMeta
{
Namespace
:
api
.
NamespaceDefault
,
},
}
// selector should match the labels in pod template.
invalidSelectorDeployment
:=
validDeployment
()
invalidSelectorDeployment
.
Spec
.
Selector
=
map
[
string
]
string
{
"name"
:
"def"
,
}
errorCases
[
"selector does not match labels"
]
=
invalidSelectorDeployment
// RestartPolicy should be always.
invalidRestartPolicyDeployment
:=
validDeployment
()
invalidRestartPolicyDeployment
.
Spec
.
Template
.
Spec
.
RestartPolicy
=
api
.
RestartPolicyNever
errorCases
[
"unsupported value 'Never'"
]
=
invalidRestartPolicyDeployment
// invalid unique label key.
invalidUniqueLabelDeployment
:=
validDeployment
()
invalidUniqueLabel
:=
"abc/def/ghi"
invalidUniqueLabelDeployment
.
Spec
.
UniqueLabelKey
=
&
invalidUniqueLabel
errorCases
[
"spec.uniqueLabel: invalid value"
]
=
invalidUniqueLabelDeployment
// rollingUpdate should be nil for recreate.
invalidRecreateDeployment
:=
validDeployment
()
invalidRecreateDeployment
.
Spec
.
Strategy
=
expapi
.
DeploymentStrategy
{
Type
:
expapi
.
DeploymentRecreate
,
RollingUpdate
:
&
expapi
.
RollingUpdateDeployment
{},
}
errorCases
[
"rollingUpdate should be nil when strategy type is Recreate"
]
=
invalidRecreateDeployment
// MaxSurge should be in the form of 20%.
invalidMaxSurgeDeployment
:=
validDeployment
()
invalidMaxSurgeDeployment
.
Spec
.
Strategy
=
expapi
.
DeploymentStrategy
{
Type
:
expapi
.
DeploymentRollingUpdate
,
RollingUpdate
:
&
expapi
.
RollingUpdateDeployment
{
MaxSurge
:
util
.
NewIntOrStringFromString
(
"20Percent"
),
},
}
errorCases
[
"value should be int(5) or percentage(5%)"
]
=
invalidMaxSurgeDeployment
// MaxSurge and MaxUnavailable cannot both be zero.
invalidRollingUpdateDeployment
:=
validDeployment
()
invalidRollingUpdateDeployment
.
Spec
.
Strategy
=
expapi
.
DeploymentStrategy
{
Type
:
expapi
.
DeploymentRollingUpdate
,
RollingUpdate
:
&
expapi
.
RollingUpdateDeployment
{
MaxSurge
:
util
.
NewIntOrStringFromString
(
"0%"
),
MaxUnavailable
:
util
.
NewIntOrStringFromInt
(
0
),
},
}
errorCases
[
"cannot be 0 when maxSurge is 0 as well"
]
=
invalidRollingUpdateDeployment
// MaxUnavailable should not be more than 100%.
invalidMaxUnavailableDeployment
:=
validDeployment
()
invalidMaxUnavailableDeployment
.
Spec
.
Strategy
=
expapi
.
DeploymentStrategy
{
Type
:
expapi
.
DeploymentRollingUpdate
,
RollingUpdate
:
&
expapi
.
RollingUpdateDeployment
{
MaxUnavailable
:
util
.
NewIntOrStringFromString
(
"110%"
),
},
}
errorCases
[
"should not be more than 100%"
]
=
invalidMaxUnavailableDeployment
for
k
,
v
:=
range
errorCases
{
errs
:=
ValidateDeployment
(
v
)
if
len
(
errs
)
==
0
{
t
.
Errorf
(
"expected failure for %s"
,
k
)
}
else
if
!
strings
.
Contains
(
errs
[
0
]
.
Error
(),
k
)
{
t
.
Errorf
(
"unexpected error: %v, expected: %s"
,
errs
[
0
],
k
)
}
}
}
pkg/util/validation.go
View file @
92f373a4
...
@@ -139,3 +139,11 @@ func IsValidPortName(port string) bool {
...
@@ -139,3 +139,11 @@ func IsValidPortName(port string) bool {
func
IsValidIPv4
(
value
string
)
bool
{
func
IsValidIPv4
(
value
string
)
bool
{
return
net
.
ParseIP
(
value
)
!=
nil
&&
net
.
ParseIP
(
value
)
.
To4
()
!=
nil
return
net
.
ParseIP
(
value
)
!=
nil
&&
net
.
ParseIP
(
value
)
.
To4
()
!=
nil
}
}
const
percentFmt
string
=
"[0-9]+%"
var
percentRegexp
=
regexp
.
MustCompile
(
"^"
+
percentFmt
+
"$"
)
func
IsValidPercent
(
percent
string
)
bool
{
return
percentRegexp
.
MatchString
(
percent
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment