-
Notifications
You must be signed in to change notification settings - Fork 472
✨ Add support for k8s:enum markers #1352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,6 +114,9 @@ func (c *schemaContext) requestSchema(pkgPath, typeName string) { | |
|
|
||
| // infoToSchema creates a schema for the type in the given set of type information. | ||
| func infoToSchema(ctx *schemaContext) *apiextensionsv1.JSONSchemaProps { | ||
| if ctx.info.Markers.Get(crdmarkers.K8sEnumTag) != nil { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is surprisingly early in the flow of the types to schema conversion Does this make sense here vs in somewhere like |
||
| return enumToSchema(ctx) | ||
| } | ||
| if obj := ctx.pkg.Types.Scope().Lookup(ctx.info.Name); obj != nil { | ||
| switch { | ||
| // If the obj implements a JSON marshaler and has a marker, use the | ||
|
|
@@ -140,6 +143,61 @@ func infoToSchema(ctx *schemaContext) *apiextensionsv1.JSONSchemaProps { | |
| return typeToSchema(ctx, ctx.info.RawSpec.Type) | ||
| } | ||
|
|
||
| func enumToSchema(ctx *schemaContext) *apiextensionsv1.JSONSchemaProps { | ||
| rawType := ctx.info.RawSpec.Type | ||
| typeDef := ctx.pkg.TypesInfo.Defs[ctx.info.RawSpec.Name] | ||
| if typeDef == nil { | ||
| ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown enum type %s", ctx.info.Name), rawType)) | ||
| return &apiextensionsv1.JSONSchemaProps{} | ||
| } | ||
| typeInfo := typeDef.Type() | ||
| if basicInfo, isBasic := typeInfo.Underlying().(*types.Basic); !isBasic || basicInfo.Info()&types.IsString == 0 { | ||
| ctx.pkg.AddError(loader.ErrFromNode(fmt.Errorf("enum type must be a string, not %s", typeInfo.String()), rawType)) | ||
| return &apiextensionsv1.JSONSchemaProps{} | ||
| } | ||
|
|
||
| var enumValues []apiextensionsv1.JSON | ||
| for _, file := range ctx.pkg.Syntax { | ||
| for _, decl := range file.Decls { | ||
| genDecl, ok := decl.(*ast.GenDecl) | ||
| if !ok || genDecl.Tok != token.CONST { | ||
| continue | ||
| } | ||
| for _, spec := range genDecl.Specs { | ||
| valueSpec, ok := spec.(*ast.ValueSpec) | ||
| if !ok { | ||
| continue | ||
| } | ||
| for i, name := range valueSpec.Names { | ||
| obj := ctx.pkg.TypesInfo.Defs[name] | ||
| if obj == nil || obj.Type() != typeInfo { | ||
| continue | ||
| } | ||
| val := valueSpec.Values[i] | ||
| basicLit, ok := val.(*ast.BasicLit) | ||
| if !ok || basicLit.Kind != token.STRING { | ||
| continue | ||
| } | ||
| // trim quotes | ||
| value := basicLit.Value[1 : len(basicLit.Value)-1] | ||
| enumValues = append(enumValues, apiextensionsv1.JSON{Raw: []byte(`"` + value + `"`)}) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| slices.SortFunc(enumValues, func(a, b apiextensionsv1.JSON) int { | ||
| return strings.Compare(string(a.Raw), string(b.Raw)) | ||
| }) | ||
|
|
||
| schema := &apiextensionsv1.JSONSchemaProps{ | ||
| Type: "string", | ||
| Enum: enumValues, | ||
| } | ||
| applyMarkers(ctx, ctx.info.Markers, schema, rawType) | ||
| return schema | ||
| } | ||
|
|
||
| type schemaMarkerWithName struct { | ||
| SchemaMarker SchemaMarker | ||
| Name string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,14 @@ type CronJobSpec struct { | |
| // +optional | ||
| ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` | ||
|
|
||
| // Specifies how to treat concurrent executions of a Job. | ||
| // Valid values are: | ||
| // - "Allow" (default): allows CronJobs to run concurrently; | ||
| // - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet; | ||
| // - "Replace": cancels currently running job and replaces it with a new one | ||
| // +optional | ||
| K8sConcurrencyPolicy K8sConcurrencyPolicy `json:"k8sConcurrencyPolicy,omitempty"` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are folks likely to put the tag on a field, does the generator error in that case? |
||
|
|
||
| // This flag tells the controller to suspend subsequent executions, it does | ||
| // not apply to already started executions. Defaults to false. | ||
| // +optional | ||
|
|
@@ -748,6 +756,21 @@ const ( | |
| ReplaceConcurrent ConcurrencyPolicy = "Replace" | ||
| ) | ||
|
|
||
| // +k8s:enum | ||
| type K8sConcurrencyPolicy string | ||
|
|
||
| const ( | ||
| // AllowK8sConcurrencyPolicy allows CronJobs to run concurrently. | ||
| AllowK8sConcurrencyPolicy K8sConcurrencyPolicy = "Allow" | ||
|
|
||
| // ForbidK8sConcurrencyPolicy forbids concurrent runs, skipping next run if previous | ||
| // hasn't finished yet. | ||
| ForbidK8sConcurrencyPolicy K8sConcurrencyPolicy = "Forbid" | ||
|
|
||
| // ReplaceK8sConcurrencyPolicy cancels currently running job and replaces it with a new one. | ||
| ReplaceK8sConcurrencyPolicy K8sConcurrencyPolicy = "Replace" | ||
| ) | ||
|
|
||
| // StringEvenType is a type that includes an expression-based validation. | ||
| // +kubebuilder:validation:XValidation:rule="self.size() % 2 == 0",message="must have even length" | ||
| type StringEvenType string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| Copyright 2024 The Kubernetes Authors. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| // +groupName=testdata.kubebuilder.io | ||
| // +versionName=v1 | ||
| package enum | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| // +k8s:enum | ||
| type EnumType string | ||
|
|
||
| const ( | ||
| Value1 EnumType = "Value1" | ||
| Value2 EnumType = "Value2" | ||
| ) | ||
|
|
||
| // +kubebuilder:object:root=true | ||
|
|
||
| // Enum is a test CRD that contains an enum. | ||
| type Enum struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
|
||
| Spec EnumSpec `json:"spec,omitempty"` | ||
| } | ||
|
|
||
| // EnumSpec defines the desired state of Enum | ||
| type EnumSpec struct { | ||
| Field EnumType `json:"field,omitempty"` | ||
| } | ||
|
|
||
| // +kubebuilder:object:root=true | ||
|
|
||
| // EnumList contains a list of Enum | ||
| type EnumList struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ListMeta `json:"metadata,omitempty"` | ||
| Items []Enum `json:"items"` | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,7 +37,11 @@ func TestCRD(t *testing.T) { | |
| } | ||
|
|
||
| var _ = BeforeSuite(func() { | ||
| testEnv = &envtest.Environment{} | ||
| testEnv = &envtest.Environment{ | ||
| CRDInstallOptions: envtest.CRDInstallOptions{ | ||
| Paths: []string{"testdata.kubebuilder.io_enums.yaml"}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do we do for other test suites? They must also install schemas right? |
||
| }, | ||
| } | ||
|
|
||
| cfg, err := testEnv.Start() | ||
| Expect(err).NotTo(HaveOccurred()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| apiVersion: apiextensions.k8s.io/v1 | ||
| kind: CustomResourceDefinition | ||
| metadata: | ||
| name: enums.testdata.kubebuilder.io | ||
| spec: | ||
| group: testdata.kubebuilder.io | ||
| names: | ||
| kind: Enum | ||
| listKind: EnumList | ||
| plural: enums | ||
| singular: enum | ||
| scope: Namespaced | ||
| versions: | ||
| - name: v1 | ||
| schema: | ||
| openAPIV3Schema: | ||
| description: Enum is a test CRD that contains an enum. | ||
| properties: | ||
| apiVersion: | ||
| description: |- | ||
| APIVersion defines the versioned schema of this representation of an object. | ||
| Servers should convert recognized schemas to the latest internal value, and | ||
| may reject unrecognized values. | ||
| More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | ||
| type: string | ||
| kind: | ||
| description: |- | ||
| Kind is a string value representing the REST resource this object represents. | ||
| Servers may infer this from the endpoint the client submits requests to. | ||
| Cannot be updated. | ||
| In CamelCase. | ||
| More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | ||
| type: string | ||
| metadata: | ||
| type: object | ||
| spec: | ||
| description: EnumSpec defines the desired state of Enum | ||
| properties: | ||
| field: | ||
| enum: | ||
| - Value1 | ||
| - Value2 | ||
| type: string | ||
| type: object | ||
| type: object | ||
| served: true | ||
| storage: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lalitc375 Do you know in DV if there would be a way to gate a new value within an enum based on some combination of
+k8s:ifEnabled?