diff --git a/processor/processor.go b/processor/processor.go
index 6939e4d..bdcf5c7 100644
--- a/processor/processor.go
+++ b/processor/processor.go
@@ -505,6 +505,14 @@ func mkRegistry(customMarkers []config.Marker) (*markers.Registry, error) {
}
}
+ // Register k8s:* markers - sig apimachinery plans to unify CRD and native types on these.
+ if err := registry.Define("k8s:required", markers.DescribesField, struct{}{}); err != nil {
+ return nil, err
+ }
+ if err := registry.Define("k8s:optional", markers.DescribesField, struct{}{}); err != nil {
+ return nil, err
+ }
+
for _, marker := range customMarkers {
t := markers.DescribesField
switch marker.Target {
@@ -554,6 +562,17 @@ func parseMarkers(markers markers.MarkerValues) (string, []string) {
validation = append(validation, fmt.Sprintf("%s: %v", name, value))
}
+ // Handle standalone +required and +k8s:required marker
+ // This is equivalent to +kubebuilder:validation:Required
+ if name == "required" || name == "k8s:required" {
+ validation = append(validation, "Required: {}")
+ }
+ // Handle standalone +optional and +k8s:optional marker
+ // This is equivalent to +kubebuilder:validation:Optional
+ if name == "optional" || name == "k8s:optional" {
+ validation = append(validation, "Optional: {}")
+ }
+
if name == "kubebuilder:default" {
if value, ok := value.(crdmarkers.Default); ok {
defaultValue = fmt.Sprintf("%v", value.Value)
diff --git a/renderer/asciidoctor.go b/renderer/asciidoctor.go
index 6598266..695f2c2 100644
--- a/renderer/asciidoctor.go
+++ b/renderer/asciidoctor.go
@@ -151,7 +151,7 @@ func (adr *AsciidoctorRenderer) TemplateValue(key string) string {
func (adr *AsciidoctorRenderer) RenderFieldDoc(text string) string {
// Escape the pipe character, which has special meaning for asciidoc as a way to format tables,
// so that including | in a comment does not result in wonky tables.
- out := strings.ReplaceAll(text, "|", "\\|")
+ out := escapePipe(text)
// Trim any leading and trailing whitespace from each line.
lines := strings.Split(out, "\n")
@@ -169,6 +169,7 @@ func (adr *AsciidoctorRenderer) RenderFieldDoc(text string) string {
func (adr *AsciidoctorRenderer) RenderValidation(text string) string {
renderedText := escapeFirstAsterixInEachPair(text)
+ renderedText = escapePipe(renderedText)
return escapeCurlyBraces(renderedText)
}
@@ -190,9 +191,15 @@ func escapeFirstAsterixInEachPair(text string) string {
return text
}
+// escapePipe ensures sufficient escapes are added to pipe characters, so they are not mistaken
+// for asciidoctor table formatting.
+func escapePipe(text string) string {
+ return strings.ReplaceAll(text, "|", "\\|")
+}
+
// escapeCurlyBraces ensures sufficient escapes are added to curly braces, so they are not mistaken
// for asciidoctor id attributes.
func escapeCurlyBraces(text string) string {
// Per asciidoctor docs, only the leading curly brace needs to be escaped.
- return strings.Replace(text, "{", "\\{", -1)
+ return strings.ReplaceAll(text, "{", "\\{")
}
diff --git a/renderer/asciidoctor_test.go b/renderer/asciidoctor_test.go
index 124f659..2fb1e92 100644
--- a/renderer/asciidoctor_test.go
+++ b/renderer/asciidoctor_test.go
@@ -19,3 +19,8 @@ func Test_escapeCurlyBraces(t *testing.T) {
assert.Equal(t, "[a-fA-F0-9]\\{64}", escapeCurlyBraces("[a-fA-F0-9]{64}"))
assert.Equal(t, "[a-fA-F0-9]\\\\{64\\}", escapeCurlyBraces("[a-fA-F0-9]\\{64\\}"))
}
+
+func Test_escapePipe(t *testing.T) {
+ assert.Equal(t, "[0-9]", escapePipe("[0-9]"))
+ assert.Equal(t, `[0-9]*\|\s`, escapePipe(`[0-9]*|\s`))
+}
diff --git a/test/api/v1/guestbook_types.go b/test/api/v1/guestbook_types.go
index 5dbdcb0..1dac6e1 100644
--- a/test/api/v1/guestbook_types.go
+++ b/test/api/v1/guestbook_types.go
@@ -124,6 +124,7 @@ type PositiveInt int
// GuestbookEntry defines an entry in a guest book.
type GuestbookEntry struct {
// Name of the guest (pipe | should be escaped)
+ // +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=80
// +kubebuilder:validation:Pattern=`0*[a-z0-9]*[a-z]*[0-9]`
Name string `json:"name,omitempty"`
@@ -146,6 +147,19 @@ type GuestbookEntry struct {
Comment string `json:"comment,omitempty"`
// Rating provided by the guest
Rating Rating `json:"rating,omitempty"`
+
+ // Email is the email address of the guest (required field using +required marker)
+ // +required
+ Email string `json:"email"`
+ // Location is the location of the guest (required field using +k8s:required marker)
+ // +k8s:required
+ Location string `json:"location"`
+ // Phone is the phone number of the guest (optional field using +optional marker)
+ // +optional
+ Phone string `json:"phone"`
+ // Company is the company of the guest (optional field using +k8s:optional marker)
+ // +k8s:optional
+ Company string `json:"company"`
}
// GuestbookStatus defines the observed state of Guestbook.
diff --git a/test/expected.asciidoc b/test/expected.asciidoc
index a5a642e..dff065f 100644
--- a/test/expected.asciidoc
+++ b/test/expected.asciidoc
@@ -162,6 +162,7 @@ GuestbookEntry defines an entry in a guest book.
| Field | Description | Default | Validation
| *`name`* __string__ | Name of the guest (pipe \| should be escaped) + | | MaxLength: 80 +
Pattern: `0\*[a-z0-9]*[a-z]*[0-9]` +
+Required: \{} +
| *`tags`* __string array__ | Tags of the entry. + | | items:Pattern: `[a-z]*` +
@@ -174,11 +175,19 @@ Now let's test a list: +
Another isolated comment. +
-Looks good? + | | Pattern: `0\*[a-z0-9]*[a-z]\*[0-9]*|\s` +
+Looks good? + | | Pattern: `0\*[a-z0-9]*[a-z]\*[0-9]*\|\s` +
| *`rating`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-rating[$$Rating$$]__ | Rating provided by the guest + | | Maximum: 5 +
Minimum: 1 +
+| *`email`* __string__ | Email is the email address of the guest (required field using +required marker) + | | Required: \{} +
+
+| *`location`* __string__ | Location is the location of the guest (required field using +k8s:required marker) + | | Required: \{} +
+
+| *`phone`* __string__ | Phone is the phone number of the guest (optional field using +optional marker) + | | Optional: \{} +
+
+| *`company`* __string__ | Company is the company of the guest (optional field using +k8s:optional marker) + | | Optional: \{} +
+
|===
diff --git a/test/expected.md b/test/expected.md
index a3678da..538adf6 100644
--- a/test/expected.md
+++ b/test/expected.md
@@ -127,11 +127,15 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
Pattern: `0*[a-z0-9]*[a-z]*[0-9]`
|
+| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
Pattern: `0*[a-z0-9]*[a-z]*[0-9]`
Required: \{\}
|
| `tags` _string array_ | Tags of the entry. | | items:Pattern: `[a-z]*`
|
| `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | | |
| `comment` _string_ | Comment by guest. This can be a multi-line comment.
Like this one.
Now let's test a list:
* a
* b
Another isolated comment.
Looks good? | | Pattern: `0*[a-z0-9]*[a-z]*[0-9]*\|\s`
|
| `rating` _[Rating](#rating)_ | Rating provided by the guest | | Maximum: 5
Minimum: 1
|
+| `email` _string_ | Email is the email address of the guest (required field using +required marker) | | Required: \{\}
|
+| `location` _string_ | Location is the location of the guest (required field using +k8s:required marker) | | Required: \{\}
|
+| `phone` _string_ | Phone is the phone number of the guest (optional field using +optional marker) | | Optional: \{\}
|
+| `company` _string_ | Company is the company of the guest (optional field using +k8s:optional marker) | | Optional: \{\}
|
#### GuestbookHeader
diff --git a/test/hide.md b/test/hide.md
index 1757e3b..d274f96 100644
--- a/test/hide.md
+++ b/test/hide.md
@@ -128,11 +128,15 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
Pattern: `0*[a-z0-9]*[a-z]*[0-9]`
|
+| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80
Pattern: `0*[a-z0-9]*[a-z]*[0-9]`
Required: \{\}
|
| `tags` _string array_ | Tags of the entry. | | items:Pattern: `[a-z]*`
|
| `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | | |
| `comment` _string_ | Comment by guest. This can be a multi-line comment.
Like this one.
Now let's test a list:
* a
* b
Another isolated comment.
Looks good? | | Pattern: `0*[a-z0-9]*[a-z]*[0-9]*\|\s`
|
| `rating` _[Rating](#rating)_ | Rating provided by the guest | | Maximum: 5
Minimum: 1
|
+| `email` _string_ | Email is the email address of the guest (required field using +required marker) | | Required: \{\}
|
+| `location` _string_ | Location is the location of the guest (required field using +k8s:required marker) | | Required: \{\}
|
+| `phone` _string_ | Phone is the phone number of the guest (optional field using +optional marker) | | Optional: \{\}
|
+| `company` _string_ | Company is the company of the guest (optional field using +k8s:optional marker) | | Optional: \{\}
|
#### GuestbookHeader