Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions pkg/crd/markers/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package markers

import (
"fmt"
"net/url"
"strings"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
Expand Down Expand Up @@ -57,6 +58,11 @@ var CRDMarkers = []*definitionWithHelp{

must(markers.MakeDefinition("kubebuilder:selectablefield", markers.DescribesType, SelectableField{})).
WithHelp(SelectableField{}.Help()),

must(markers.MakeDefinition("kubebuilder:externalDocs", markers.DescribesField, ExternalDocs{})).
WithHelp(ExternalDocs{}.Help()),
must(markers.MakeDefinition("kubebuilder:externalDocs", markers.DescribesType, ExternalDocs{})).
WithHelp(ExternalDocs{}.Help()),
}

// TODO: categories and singular used to be annotations types
Expand Down Expand Up @@ -419,3 +425,28 @@ func (s SelectableField) ApplyToCRD(crd *apiextensionsv1.CustomResourceDefinitio

return nil
}

// +controllertools:marker:generateHelp:category=CRD

// ExternalDocs specifies external documentation for this field or type.
//
// The url is required and must be a valid URL. The description is optional
// and provides a short description of the external documentation.
type ExternalDocs struct {
// URL specifies the URL for the target documentation.
URL string `marker:"url"`

// Description is a short description of the target documentation.
Description string `marker:",optional"`
}

func (m ExternalDocs) ApplyToSchema(schema *apiextensionsv1.JSONSchemaProps) error {
if _, err := url.ParseRequestURI(m.URL); err != nil {
return fmt.Errorf("invalid url %q in kubebuilder:externalDocs marker: %w", m.URL, err)
}
schema.ExternalDocs = &apiextensionsv1.ExternalDocumentation{
URL: m.URL,
Description: m.Description,
}
return nil
}
20 changes: 20 additions & 0 deletions pkg/crd/markers/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions pkg/crd/parser_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ var _ = Describe("CRD Generation From Parsing to CustomResourceDefinition", func
})
})

Context("ExternalDoc API", func() {
BeforeEach(func() {
pkgPaths = []string{"./external_docs/..."}
expPkgLen = 1
})
It("should successfully generate the CRD with external documentation", func() {
assertCRD(pkgs[0], "ExternalDoc", "testdata.kubebuilder.io_externaldocs.yaml")
})
})

Context("CronJob API without group", func() {
BeforeEach(func() {
pkgPaths = []string{"./nogroup"}
Expand Down
59 changes: 59 additions & 0 deletions pkg/crd/testdata/external_docs/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*

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 external_docs

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +kubebuilder:object:root=true

// ExternalDocSpec defines the desired state of ExternalDoc
type ExternalDocSpec struct {
// This tests that external documentation can be attached to a field with url and description.
// +kubebuilder:externalDocs:url="https://example.com/docs",description="external docs description"
FieldWithExternalDoc string `json:"fieldWithExternalDoc,omitempty"`

// This tests that external documentation can be attached with only url.
// +kubebuilder:externalDocs:url="https://example.com/docs"
FieldWithExternalDocURLOnly string `json:"fieldWithExternalDocURLOnly,omitempty"`

// This tests that external documentation from a type is propagated.
TypeWithExternalDoc TypeWithExternalDoc `json:"typeWithExternalDoc,omitempty"`
}

// TypeWithExternalDoc is a type with external documentation.
// +kubebuilder:externalDocs:url="https://example.com/type-docs",description="type-level external docs"
type TypeWithExternalDoc string

// ExternalDoc is the Schema for the external docs API
type ExternalDoc struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ExternalDocSpec `json:"spec,omitempty"`
}

// +kubebuilder:object:root=true

// ExternalDocList contains a list of ExternalDoc
type ExternalDocList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ExternalDoc `json:"items"`
}
65 changes: 65 additions & 0 deletions pkg/crd/testdata/testdata.kubebuilder.io_externaldocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
name: externaldocs.testdata.kubebuilder.io
spec:
group: testdata.kubebuilder.io
names:
kind: ExternalDoc
listKind: ExternalDocList
plural: externaldocs
singular: externaldoc
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ExternalDoc is the Schema for the external docs API
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: ExternalDocSpec defines the desired state of ExternalDoc
properties:
fieldWithExternalDoc:
description: This tests that external documentation can be attached
to a field with url and description.
externalDocs:
description: external docs description
url: https://example.com/docs
type: string
fieldWithExternalDocURLOnly:
description: This tests that external documentation can be attached
with only url.
externalDocs:
url: https://example.com/docs
type: string
typeWithExternalDoc:
description: This tests that external documentation from a type is
propagated.
externalDocs:
description: type-level external docs
url: https://example.com/type-docs
type: string
type: object
type: object
served: true
storage: true