Skip to content

Commit

Permalink
Support api-approved annotation for CRD with k8s group
Browse files Browse the repository at this point in the history
  • Loading branch information
FillZpp committed Jun 14, 2022
1 parent d97fa93 commit e8aea0c
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 9 deletions.
30 changes: 30 additions & 0 deletions pkg/crd/markers/crd.go
Expand Up @@ -19,6 +19,7 @@ package markers
import (
"fmt"

"k8s.io/apiextensions-apiserver/pkg/apihelpers"
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

"sigs.k8s.io/controller-tools/pkg/markers"
Expand Down Expand Up @@ -51,6 +52,9 @@ var CRDMarkers = []*definitionWithHelp{

must(markers.MakeDefinition("kubebuilder:deprecatedversion", markers.DescribesType, DeprecatedVersion{})).
WithHelp(DeprecatedVersion{}.Help()),

must(markers.MakeDefinition("kubebuilder:apiapprovedk8sgroup", markers.DescribesType, APIApprovedKubernetesGroup{})).
WithHelp(APIApprovedKubernetesGroup{}.Help()),
}

// TODO: categories and singular used to be annotations types
Expand Down Expand Up @@ -345,3 +349,29 @@ func (s DeprecatedVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec,
}
return nil
}

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

// APIApprovedKubernetesGroup configures the api-approved.kubernetes.io for a CRD
// with Kubernetes groups, including "k8s.io", "*.k8s.io", "kubernetes.io", "*.kubernetes.io".
type APIApprovedKubernetesGroup struct {
// URL is the value of api-approved.kubernetes.io annotation on CRD,
// it should be a valid URL which contains Scheme and Host and
// should not start with "unapproved".
URL string `marker:"URL"`
}

func (s APIApprovedKubernetesGroup) ApplyToCRD(crd *apiext.CustomResourceDefinition, version string) error {
if crd.Annotations == nil {
crd.Annotations = map[string]string{}
}
crd.Annotations[apiext.KubeAPIApprovedAnnotation] = s.URL

// validate if the annotation is valid
state, reason := apihelpers.GetAPIApprovalState(crd.Annotations)
if state != apihelpers.APIApproved {
return fmt.Errorf("invalid URL %s that is not approved: %v", s.URL, reason)
}

return nil
}
16 changes: 16 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.

7 changes: 5 additions & 2 deletions pkg/crd/parser_integration_test.go
Expand Up @@ -122,8 +122,11 @@ var _ = Describe("CRD Generation From Parsing to CustomResourceDefinition", func
By(fmt.Sprintf("parsing the desired %s YAML", kind))
var crd apiext.CustomResourceDefinition
ExpectWithOffset(1, yaml.Unmarshal(expectedFile, &crd)).To(Succeed())
// clear the annotations -- we don't care about the attribution annotation
crd.Annotations = nil
// clear the version annotation -- we don't care about the attribution annotation
delete(crd.Annotations, "controller-gen.kubebuilder.io/version")
if len(crd.Annotations) == 0 {
crd.Annotations = nil
}

By(fmt.Sprintf("comparing the two %s CRDs", kind))
ExpectWithOffset(1, parser.CustomResourceDefinitions[groupKind]).To(Equal(crd), "type not as expected, check pkg/crd/testdata/README.md for more details.\n\nDiff:\n\n%s", cmp.Diff(parser.CustomResourceDefinitions[groupKind], crd))
Expand Down
24 changes: 17 additions & 7 deletions pkg/crd/spec.go
Expand Up @@ -30,13 +30,21 @@ import (
)

// SpecMarker is a marker that knows how to apply itself to a particular
// version in a CRD.
// version in a CRD Spec.
type SpecMarker interface {
// ApplyToCRD applies this marker to the given CRD, in the given version
// within that CRD. It's called after everything else in the CRD is populated.
ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error
}

// Marker is a marker that knows how to apply itself to a particular
// version in a CRD.
type Marker interface {
// ApplyToCRD applies this marker to the given CRD, in the given version
// within that CRD. It's called after everything else in the CRD is populated.
ApplyToCRD(crd *apiext.CustomResourceDefinition, version string) error
}

// NeedCRDFor requests the full CRD for the given group-kind. It requires
// that the packages containing the Go structs for that CRD have already
// been loaded with NeedPackage.
Expand Down Expand Up @@ -109,12 +117,14 @@ func (p *Parser) NeedCRDFor(groupKind schema.GroupKind, maxDescLen *int) {

for _, markerVals := range typeInfo.Markers {
for _, val := range markerVals {
crdMarker, isCrdMarker := val.(SpecMarker)
if !isCrdMarker {
continue
}
if err := crdMarker.ApplyToCRD(&crd.Spec, ver); err != nil {
pkg.AddError(loader.ErrFromNode(err /* an okay guess */, typeInfo.RawSpec))
if specMarker, isSpecMarker := val.(SpecMarker); isSpecMarker {
if err := specMarker.ApplyToCRD(&crd.Spec, ver); err != nil {
pkg.AddError(loader.ErrFromNode(err /* an okay guess */, typeInfo.RawSpec))
}
} else if crdMarker, isCRDMarker := val.(Marker); isCRDMarker {
if err := crdMarker.ApplyToCRD(&crd, ver); err != nil {
pkg.AddError(loader.ErrFromNode(err /* an okay guess */, typeInfo.RawSpec))
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/crd/testdata/cronjob_types.go
Expand Up @@ -478,6 +478,7 @@ type CronJobStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:resource:singular=mycronjob
// +kubebuilder:storageversion
// +kubebuilder:apiapprovedk8sgroup:URL="https://github.com/kubernetes-sigs/controller-tools"

// CronJob is the Schema for the cronjobs API
type CronJob struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml
Expand Up @@ -4,6 +4,7 @@ kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/controller-tools
creationTimestamp: null
name: cronjobs.testdata.kubebuilder.io
spec:
Expand Down

0 comments on commit e8aea0c

Please sign in to comment.