mirror of https://github.com/k3s-io/k3s
Allow CRD /scale labelSelectors to be under either status or spec (#78234)
* apiextensions API doc: allow labelSelectorPath to be under .status or .spec * apiextensions validation: allow labelSelectorPath to be under .status or .spec * test * generatedk3s-v1.15.3
parent
470916d32d
commit
6a5fa6ca5b
|
@ -16616,7 +16616,7 @@
|
|||
"description": "CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.",
|
||||
"properties": {
|
||||
"labelSelectorPath": {
|
||||
"description": "LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector. Only JSON paths without the array notation are allowed. Must be a JSON Path under .status. Must be set to work with HPA. If there is no value under the given path in the CustomResource, the status label selector value in the /scale subresource will default to the empty string.",
|
||||
"description": "LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector. Only JSON paths without the array notation are allowed. Must be a JSON Path under .status or .spec. Must be set to work with HPA. The field pointed by this JSON path must be a string field (not a complex selector struct) which contains a serialized label selector in string form. More info: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource If there is no value under the given path in the CustomResource, the status label selector value in the /scale subresource will default to the empty string.",
|
||||
"type": "string"
|
||||
},
|
||||
"specReplicasPath": {
|
||||
|
|
|
@ -393,8 +393,11 @@ type CustomResourceSubresourceScale struct {
|
|||
StatusReplicasPath string
|
||||
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
|
||||
// Only JSON paths without the array notation are allowed.
|
||||
// Must be a JSON Path under .status.
|
||||
// Must be a JSON Path under .status or .spec.
|
||||
// Must be set to work with HPA.
|
||||
// The field pointed by this JSON path must be a string field (not a complex selector struct)
|
||||
// which contains a serialized label selector in string form.
|
||||
// More info: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource
|
||||
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
|
||||
// subresource will default to the empty string.
|
||||
// +optional
|
||||
|
|
|
@ -327,8 +327,11 @@ message CustomResourceSubresourceScale {
|
|||
|
||||
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
|
||||
// Only JSON paths without the array notation are allowed.
|
||||
// Must be a JSON Path under .status.
|
||||
// Must be a JSON Path under .status or .spec.
|
||||
// Must be set to work with HPA.
|
||||
// The field pointed by this JSON path must be a string field (not a complex selector struct)
|
||||
// which contains a serialized label selector in string form.
|
||||
// More info: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource
|
||||
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
|
||||
// subresource will default to the empty string.
|
||||
// +optional
|
||||
|
|
|
@ -412,8 +412,11 @@ type CustomResourceSubresourceScale struct {
|
|||
StatusReplicasPath string `json:"statusReplicasPath" protobuf:"bytes,2,opt,name=statusReplicasPath"`
|
||||
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
|
||||
// Only JSON paths without the array notation are allowed.
|
||||
// Must be a JSON Path under .status.
|
||||
// Must be a JSON Path under .status or .spec.
|
||||
// Must be set to work with HPA.
|
||||
// The field pointed by this JSON path must be a string field (not a complex selector struct)
|
||||
// which contains a serialized label selector in string form.
|
||||
// More info: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource
|
||||
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
|
||||
// subresource will default to the empty string.
|
||||
// +optional
|
||||
|
|
|
@ -794,8 +794,8 @@ func ValidateCustomResourceDefinitionSubresources(subresources *apiextensions.Cu
|
|||
if subresources.Scale.LabelSelectorPath != nil && len(*subresources.Scale.LabelSelectorPath) > 0 {
|
||||
if errs := validateSimpleJSONPath(*subresources.Scale.LabelSelectorPath, fldPath.Child("scale.labelSelectorPath")); len(errs) > 0 {
|
||||
allErrs = append(allErrs, errs...)
|
||||
} else if !strings.HasPrefix(*subresources.Scale.LabelSelectorPath, ".status.") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("scale.labelSelectorPath"), subresources.Scale.LabelSelectorPath, "should be a json path under .status"))
|
||||
} else if !strings.HasPrefix(*subresources.Scale.LabelSelectorPath, ".spec.") && !strings.HasPrefix(*subresources.Scale.LabelSelectorPath, ".status.") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("scale.labelSelectorPath"), subresources.Scale.LabelSelectorPath, "should be a json path under either .spec or .status"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1162,6 +1162,83 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
|||
required("spec", "versions[1]", "schema", "openAPIV3Schema"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "labelSelectorPath outside of .spec and .status",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version0",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
// null labelSelectorPath
|
||||
Name: "version0",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
Subresources: &apiextensions.CustomResourceSubresources{
|
||||
Scale: &apiextensions.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: ".spec.replicas",
|
||||
StatusReplicasPath: ".status.replicas",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// labelSelectorPath under .status
|
||||
Name: "version1",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Subresources: &apiextensions.CustomResourceSubresources{
|
||||
Scale: &apiextensions.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: ".spec.replicas",
|
||||
StatusReplicasPath: ".status.replicas",
|
||||
LabelSelectorPath: strPtr(".status.labelSelector"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// labelSelectorPath under .spec
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Subresources: &apiextensions.CustomResourceSubresources{
|
||||
Scale: &apiextensions.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: ".spec.replicas",
|
||||
StatusReplicasPath: ".status.replicas",
|
||||
LabelSelectorPath: strPtr(".spec.labelSelector"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// labelSelectorPath outside of .spec and .status
|
||||
Name: "version3",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Subresources: &apiextensions.CustomResourceSubresources{
|
||||
Scale: &apiextensions.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: ".spec.replicas",
|
||||
StatusReplicasPath: ".status.replicas",
|
||||
LabelSelectorPath: strPtr(".labelSelector"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version0"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("spec", "versions[3]", "subresources", "scale", "labelSelectorPath"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
|
Loading…
Reference in New Issue