mirror of https://github.com/k3s-io/k3s
add RFC952 label validation
parent
15008e1390
commit
e2365b1f96
|
@ -18,7 +18,9 @@ Label selectors may also be used to associate policies with sets of objects.
|
||||||
|
|
||||||
We also [plan](https://github.com/GoogleCloudPlatform/kubernetes/issues/560) to make labels available inside pods and [lifecycle hooks](container-environment.md).
|
We also [plan](https://github.com/GoogleCloudPlatform/kubernetes/issues/560) to make labels available inside pods and [lifecycle hooks](container-environment.md).
|
||||||
|
|
||||||
[Namespacing of label keys](https://github.com/GoogleCloudPlatform/kubernetes/issues/1491) and [character/syntax restrictions](https://github.com/GoogleCloudPlatform/kubernetes/issues/1297) are under discussion.
|
[Namespacing of label keys](https://github.com/GoogleCloudPlatform/kubernetes/issues/1491) is under discussion.
|
||||||
|
|
||||||
|
Valid labels follow a slightly modified RFC952 format: 24 characters or less, all lowercase, begins with alpha, dashes (-) are allowed, and ends with alphanumeric.
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
|
|
|
@ -339,6 +339,17 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", pod.Namespace))
|
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", pod.Namespace))
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, ValidatePodState(&pod.DesiredState).Prefix("desiredState")...)
|
allErrs = append(allErrs, ValidatePodState(&pod.DesiredState).Prefix("desiredState")...)
|
||||||
|
allErrs = append(allErrs, validateLabels(pod.Labels)...)
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLabels(labels map[string]string) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
for k := range labels {
|
||||||
|
if !util.IsDNS952Label(k) {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldNotSupported("label", k))
|
||||||
|
}
|
||||||
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,6 +403,8 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
|
||||||
if labels.Set(service.Selector).AsSelector().Empty() {
|
if labels.Set(service.Selector).AsSelector().Empty() {
|
||||||
allErrs = append(allErrs, errs.NewFieldRequired("selector", service.Selector))
|
allErrs = append(allErrs, errs.NewFieldRequired("selector", service.Selector))
|
||||||
}
|
}
|
||||||
|
allErrs = append(allErrs, validateLabels(service.Labels)...)
|
||||||
|
allErrs = append(allErrs, validateLabels(service.Selector)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,6 +418,7 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", controller.Namespace))
|
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", controller.Namespace))
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, ValidateReplicationControllerState(&controller.DesiredState).Prefix("desiredState")...)
|
allErrs = append(allErrs, ValidateReplicationControllerState(&controller.DesiredState).Prefix("desiredState")...)
|
||||||
|
allErrs = append(allErrs, validateLabels(controller.Labels)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +433,7 @@ func ValidateReplicationControllerState(state *api.ReplicationControllerState) e
|
||||||
if !selector.Matches(labels) {
|
if !selector.Matches(labels) {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate))
|
allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate))
|
||||||
}
|
}
|
||||||
|
allErrs = append(allErrs, validateLabels(labels)...)
|
||||||
if state.Replicas < 0 {
|
if state.Replicas < 0 {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas))
|
allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas))
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,6 +421,27 @@ func TestValidatePod(t *testing.T) {
|
||||||
if len(errs) != 1 {
|
if len(errs) != 1 {
|
||||||
t.Errorf("Unexpected error list: %#v", errs)
|
t.Errorf("Unexpected error list: %#v", errs)
|
||||||
}
|
}
|
||||||
|
errs = ValidatePod(&api.Pod{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"123cantbeginwithnumber": "bar", //invalid
|
||||||
|
"NoUppercase123": "bar", //invalid
|
||||||
|
"nospecialchars^=@": "bar", //invalid
|
||||||
|
"cantendwithadash-": "bar", //invalid
|
||||||
|
"rfc952-mustbe24charactersorless": "bar", //invalid
|
||||||
|
"rfc952-dash-nodots-lower": "bar", //good label
|
||||||
|
"rfc952-24chars-orless": "bar", //good label
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DesiredState: api.PodState{
|
||||||
|
Manifest: api.ContainerManifest{Version: "v1beta1", ID: "abc"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if len(errs) != 5 {
|
||||||
|
t.Errorf("Unexpected non-zero error list: %#v", errs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidatePodUpdate(t *testing.T) {
|
func TestValidatePodUpdate(t *testing.T) {
|
||||||
|
@ -706,6 +727,21 @@ func TestValidateService(t *testing.T) {
|
||||||
},
|
},
|
||||||
numErrs: 0,
|
numErrs: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid label",
|
||||||
|
svc: api.Service{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "abc123",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Port: 80,
|
||||||
|
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
|
||||||
|
},
|
||||||
|
numErrs: 2,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
@ -747,6 +783,15 @@ func TestValidateReplicationController(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||||
|
invalidPodTemplate := api.PodTemplate{
|
||||||
|
DesiredState: api.PodState{
|
||||||
|
Manifest: api.ContainerManifest{
|
||||||
|
Version: "v1beta1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Labels: invalidSelector,
|
||||||
|
}
|
||||||
successCases := []api.ReplicationController{
|
successCases := []api.ReplicationController{
|
||||||
{
|
{
|
||||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||||
|
@ -817,6 +862,31 @@ func TestValidateReplicationController(t *testing.T) {
|
||||||
ReplicaSelector: validSelector,
|
ReplicaSelector: validSelector,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"invalid_label": {
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "abc-123",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DesiredState: api.ReplicationControllerState{
|
||||||
|
ReplicaSelector: validSelector,
|
||||||
|
PodTemplate: validPodTemplate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"invalid_label 2": {
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "abc-123",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DesiredState: api.ReplicationControllerState{
|
||||||
|
PodTemplate: invalidPodTemplate,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
errs := ValidateReplicationController(&v)
|
errs := ValidateReplicationController(&v)
|
||||||
|
@ -830,7 +900,9 @@ func TestValidateReplicationController(t *testing.T) {
|
||||||
field != "namespace" &&
|
field != "namespace" &&
|
||||||
field != "desiredState.replicaSelector" &&
|
field != "desiredState.replicaSelector" &&
|
||||||
field != "GCEPersistentDisk.ReadOnly" &&
|
field != "GCEPersistentDisk.ReadOnly" &&
|
||||||
field != "desiredState.replicas" {
|
field != "desiredState.replicas" &&
|
||||||
|
field != "desiredState.label" &&
|
||||||
|
field != "label" {
|
||||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,6 @@ func (r RealPodControl) createReplica(ctx api.Context, controllerSpec api.Replic
|
||||||
for k, v := range controllerSpec.DesiredState.PodTemplate.Labels {
|
for k, v := range controllerSpec.DesiredState.PodTemplate.Labels {
|
||||||
desiredLabels[k] = v
|
desiredLabels[k] = v
|
||||||
}
|
}
|
||||||
desiredLabels["replicationController"] = controllerSpec.Name
|
|
||||||
|
|
||||||
pod := &api.Pod{
|
pod := &api.Pod{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Labels: desiredLabels,
|
Labels: desiredLabels,
|
||||||
|
|
Loading…
Reference in New Issue