Repository: crossplane/crossplane

Issue:
[v2] Promote `CompositeResourceDefinition` v2alpha1 to v2
<!--
Thank you for helping to improve Crossplane!

Please be sure to search for open issues before raising a new one. We use issues
for bug reports and feature requests. Please find us at https://slack.crossplane.io
for questions, support, and discussion.
-->

### What problem are you facing?
<!--
Please tell us a little about your use case - it's okay if it's hypothetical!
Leading with this context helps frame the feature request so we can ensure we
implement it sensibly.
--->

https://github.com/crossplane/crossplane/pull/6489 introduces a new v2alpha1 XRD type alongside the v1 type, which is kept for backward compatibility.

When we ship Crossplane v2.0 I think we'll want to go straight to v2 - not v2alpha1. The new XRD is mostly an evolution so I don't think it needs to go alpha, beta, etc.

### How could Crossplane help solve your problem?
<!--
Let us know how you think Crossplane could help with your use case. 
-->

Bump the type to v2 before we ship v2.0.

Hints from issue comments:
@negz: When we do this we may also want to update the XRD reconciler to reconcile the v2 type, not the v1 type.

Per https://github.com/crossplane/crossplane/blob/main/contributing/guide-api-promotion.md I don't think we can make it the storage version in v2 though.@lsviben: I can take this one. 

Solution patch:
diff --git a/apis/apiextensions/apiextensions.go b/apis/apiextensions/apiextensions.go
index 88afbe8f5..fd9ded06d 100644
--- a/apis/apiextensions/apiextensions.go
+++ b/apis/apiextensions/apiextensions.go
@@ -23,6 +23,7 @@ import (
 	v1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
 	"github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
 	"github.com/crossplane/crossplane/apis/apiextensions/v1beta1"
+	v2 "github.com/crossplane/crossplane/apis/apiextensions/v2"
 	"github.com/crossplane/crossplane/apis/apiextensions/v2alpha1"
 )
 
@@ -33,6 +34,7 @@ func init() {
 		v1beta1.AddToScheme,
 		v1alpha1.AddToScheme,
 		v2alpha1.AddToScheme,
+		v2.AddToScheme,
 	)
 }
 
diff --git a/apis/apiextensions/v1/xrd_types.go b/apis/apiextensions/v1/xrd_types.go
index a14843510..2de2d306a 100644
--- a/apis/apiextensions/v1/xrd_types.go
+++ b/apis/apiextensions/v1/xrd_types.go
@@ -252,6 +252,7 @@ type CompositeResourceDefinitionControllerStatus struct {
 // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
 // +kubebuilder:subresource:status
 // +kubebuilder:resource:scope=Cluster,categories=crossplane,shortName=xrd;xrds
+// +kubebuilder:deprecatedversion:warning="CompositeResourceDefinition v1 is deprecated and will be removed in a future release; consider migrating to v2"
 type CompositeResourceDefinition struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/apiextensions/v2/doc.go b/apis/apiextensions/v2/doc.go
new file mode 100644
index 000000000..99ff9f2cd
--- /dev/null
+++ b/apis/apiextensions/v2/doc.go
@@ -0,0 +1,21 @@
+/*
+Copyright 2025 The Crossplane 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.
+*/
+
+// Package v2 contains API types that extend the Crossplane API.
+// +kubebuilder:object:generate=true
+// +groupName=apiextensions.crossplane.io
+// +versionName=v2
+package v2
diff --git a/apis/apiextensions/v2/register.go b/apis/apiextensions/v2/register.go
new file mode 100644
index 000000000..c66580cee
--- /dev/null
+++ b/apis/apiextensions/v2/register.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2025 The Crossplane 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.
+*/
+
+package v2
+
+import (
+	"reflect"
+
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"sigs.k8s.io/controller-runtime/pkg/scheme"
+)
+
+// Package type metadata.
+const (
+	Group   = "apiextensions.crossplane.io"
+	Version = "v2"
+)
+
+var (
+	// SchemeGroupVersion is group version used to register these objects.
+	SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version}
+
+	// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+	SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
+
+	// AddToScheme adds all registered types to scheme.
+	AddToScheme = SchemeBuilder.AddToScheme
+)
+
+// CompositeResourceDefinition type metadata.
+var (
+	CompositeResourceDefinitionKind             = reflect.TypeOf(CompositeResourceDefinition{}).Name()
+	CompositeResourceDefinitionGroupKind        = schema.GroupKind{Group: Group, Kind: CompositeResourceDefinitionKind}.String()
+	CompositeResourceDefinitionKindAPIVersion   = CompositeResourceDefinitionKind + "." + SchemeGroupVersion.String()
+	CompositeResourceDefinitionGroupVersionKind = SchemeGroupVersion.WithKind(CompositeResourceDefinitionKind)
+)
+
+func init() {
+	SchemeBuilder.Register(
+		&CompositeResourceDefinition{}, &CompositeResourceDefinitionList{},
+	)
+}
diff --git a/apis/apiextensions/v2alpha1/xrd_types.go b/apis/apiextensions/v2/xrd_types.go
similarity index 99%
rename from apis/apiextensions/v2alpha1/xrd_types.go
rename to apis/apiextensions/v2/xrd_types.go
index c0c8aab66..1bee4ba1d 100644
--- a/apis/apiextensions/v2alpha1/xrd_types.go
+++ b/apis/apiextensions/v2/xrd_types.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Crossplane Authors.
+Copyright 2025 The Crossplane Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package v2alpha1
+package v2
 
 import (
 	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -271,7 +271,6 @@ type CompositeResourceDefinitionList struct {
 // the composite resource this CompositeResourceDefinition defines.
 func (c *CompositeResourceDefinition) GetCompositeGroupVersionKind() schema.GroupVersionKind {
 	v := ""
-
 	for _, vr := range c.Spec.Versions {
 		if vr.Referenceable {
 			v = vr.Name
@@ -297,7 +296,6 @@ func (c *CompositeResourceDefinition) GetClaimGroupVersionKind() schema.GroupVer
 	}
 
 	v := ""
-
 	for _, vr := range c.Spec.Versions {
 		if vr.Referenceable {
 			v = vr.Name
diff --git a/apis/apiextensions/v2/zz_generated.deepcopy.go b/apis/apiextensions/v2/zz_generated.deepcopy.go
new file mode 100644
index 000000000..32b1ebfc0
--- /dev/null
+++ b/apis/apiextensions/v2/zz_generated.deepcopy.go
@@ -0,0 +1,293 @@
+//go:build !ignore_autogenerated
+
+/*
+Copyright 2021 The Crossplane 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.
+*/
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package v2
+
+import (
+	"github.com/crossplane/crossplane-runtime/apis/common/v1"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinition) DeepCopyInto(out *CompositeResourceDefinition) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinition.
+func (in *CompositeResourceDefinition) DeepCopy() *CompositeResourceDefinition {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinition)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *CompositeResourceDefinition) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionControllerStatus) DeepCopyInto(out *CompositeResourceDefinitionControllerStatus) {
+	*out = *in
+	out.CompositeResourceTypeRef = in.CompositeResourceTypeRef
+	out.CompositeResourceClaimTypeRef = in.CompositeResourceClaimTypeRef
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionControllerStatus.
+func (in *CompositeResourceDefinitionControllerStatus) DeepCopy() *CompositeResourceDefinitionControllerStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionControllerStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionList) DeepCopyInto(out *CompositeResourceDefinitionList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]CompositeResourceDefinition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionList.
+func (in *CompositeResourceDefinitionList) DeepCopy() *CompositeResourceDefinitionList {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *CompositeResourceDefinitionList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionSpec) DeepCopyInto(out *CompositeResourceDefinitionSpec) {
+	*out = *in
+	in.Names.DeepCopyInto(&out.Names)
+	if in.DefaultCompositionRef != nil {
+		in, out := &in.DefaultCompositionRef, &out.DefaultCompositionRef
+		*out = new(CompositionReference)
+		**out = **in
+	}
+	if in.EnforcedCompositionRef != nil {
+		in, out := &in.EnforcedCompositionRef, &out.EnforcedCompositionRef
+		*out = new(CompositionReference)
+		**out = **in
+	}
+	if in.DefaultCompositionUpdatePolicy != nil {
+		in, out := &in.DefaultCompositionUpdatePolicy, &out.DefaultCompositionUpdatePolicy
+		*out = new(v1.UpdatePolicy)
+		**out = **in
+	}
+	if in.Versions != nil {
+		in, out := &in.Versions, &out.Versions
+		*out = make([]CompositeResourceDefinitionVersion, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.Conversion != nil {
+		in, out := &in.Conversion, &out.Conversion
+		*out = new(apiextensionsv1.CustomResourceConversion)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Metadata != nil {
+		in, out := &in.Metadata, &out.Metadata
+		*out = new(CompositeResourceDefinitionSpecMetadata)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ClaimNames != nil {
+		in, out := &in.ClaimNames, &out.ClaimNames
+		*out = new(apiextensionsv1.CustomResourceDefinitionNames)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.DefaultCompositeDeletePolicy != nil {
+		in, out := &in.DefaultCompositeDeletePolicy, &out.DefaultCompositeDeletePolicy
+		*out = new(v1.CompositeDeletePolicy)
+		**out = **in
+	}
+	if in.ConnectionSecretKeys != nil {
+		in, out := &in.ConnectionSecretKeys, &out.ConnectionSecretKeys
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionSpec.
+func (in *CompositeResourceDefinitionSpec) DeepCopy() *CompositeResourceDefinitionSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionSpecMetadata) DeepCopyInto(out *CompositeResourceDefinitionSpecMetadata) {
+	*out = *in
+	if in.Labels != nil {
+		in, out := &in.Labels, &out.Labels
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	if in.Annotations != nil {
+		in, out := &in.Annotations, &out.Annotations
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionSpecMetadata.
+func (in *CompositeResourceDefinitionSpecMetadata) DeepCopy() *CompositeResourceDefinitionSpecMetadata {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionSpecMetadata)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionStatus) DeepCopyInto(out *CompositeResourceDefinitionStatus) {
+	*out = *in
+	in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
+	out.Controllers = in.Controllers
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionStatus.
+func (in *CompositeResourceDefinitionStatus) DeepCopy() *CompositeResourceDefinitionStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceDefinitionVersion) DeepCopyInto(out *CompositeResourceDefinitionVersion) {
+	*out = *in
+	if in.Deprecated != nil {
+		in, out := &in.Deprecated, &out.Deprecated
+		*out = new(bool)
+		**out = **in
+	}
+	if in.DeprecationWarning != nil {
+		in, out := &in.DeprecationWarning, &out.DeprecationWarning
+		*out = new(string)
+		**out = **in
+	}
+	if in.Schema != nil {
+		in, out := &in.Schema, &out.Schema
+		*out = new(CompositeResourceValidation)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.AdditionalPrinterColumns != nil {
+		in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
+		*out = make([]apiextensionsv1.CustomResourceColumnDefinition, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionVersion.
+func (in *CompositeResourceDefinitionVersion) DeepCopy() *CompositeResourceDefinitionVersion {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceDefinitionVersion)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositeResourceValidation) DeepCopyInto(out *CompositeResourceValidation) {
+	*out = *in
+	in.OpenAPIV3Schema.DeepCopyInto(&out.OpenAPIV3Schema)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceValidation.
+func (in *CompositeResourceValidation) DeepCopy() *CompositeResourceValidation {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositeResourceValidation)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CompositionReference) DeepCopyInto(out *CompositionReference) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositionReference.
+func (in *CompositionReference) DeepCopy() *CompositionReference {
+	if in == nil {
+		return nil
+	}
+	out := new(CompositionReference)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TypeReference) DeepCopyInto(out *TypeReference) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeReference.
+func (in *TypeReference) DeepCopy() *TypeReference {
+	if in == nil {
+		return nil
+	}
+	out := new(TypeReference)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/apis/apiextensions/v2alpha1/conditions.go b/apis/apiextensions/v2alpha1/conditions.go
deleted file mode 100644
index 4b9d77efb..000000000
--- a/apis/apiextensions/v2alpha1/conditions.go
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-Copyright 2020 The Crossplane 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.
-*/
-
-package v2alpha1
-
-import (
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
-	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
-)
-
-// Condition types.
-const (
-	// A TypeEstablished XRD has created the CRD for its composite resource and
-	// started a controller to reconcile instances of said resource.
-	TypeEstablished xpv1.ConditionType = "Established"
-
-	// A TypeOffered XRD has created the CRD for its composite resource claim
-	// and started a controller to reconcile instances of said claim.
-	TypeOffered xpv1.ConditionType = "Offered"
-)
-
-// Reasons a resource is or is not established or offered.
-const (
-	ReasonWatchingComposite xpv1.ConditionReason = "WatchingCompositeResource"
-	ReasonWatchingClaim     xpv1.ConditionReason = "WatchingCompositeResourceClaim"
-
-	ReasonTerminatingComposite xpv1.ConditionReason = "TerminatingCompositeResource"
-	ReasonTerminatingClaim     xpv1.ConditionReason = "TerminatingCompositeResourceClaim"
-)
-
-// WatchingComposite indicates that Crossplane has defined and is watching for a
-// new kind of composite resource.
-func WatchingComposite() xpv1.Condition {
-	return xpv1.Condition{
-		Type:               TypeEstablished,
-		Status:             corev1.ConditionTrue,
-		LastTransitionTime: metav1.Now(),
-		Reason:             ReasonWatchingComposite,
-	}
-}
-
-// TerminatingComposite indicates that Crossplane is terminating the controller
-// for and removing the definition of a composite resource.
-func TerminatingComposite() xpv1.Condition {
-	return xpv1.Condition{
-		Type:               TypeEstablished,
-		Status:             corev1.ConditionFalse,
-		LastTransitionTime: metav1.Now(),
-		Reason:             ReasonTerminatingComposite,
-	}
-}
-
-// WatchingClaim indicates that Crossplane has defined and is watching for a
-// new kind of composite resource claim.
-func WatchingClaim() xpv1.Condition {
-	return xpv1.Condition{
-		Type:               TypeOffered,
-		Status:             corev1.ConditionTrue,
-		LastTransitionTime: metav1.Now(),
-		Reason:             ReasonWatchingClaim,
-	}
-}
-
-// TerminatingClaim indicates that Crossplane is terminating the controller and
-// removing the definition of a composite resource claim.
-func TerminatingClaim() xpv1.Condition {
-	return xpv1.Condition{
-		Type:               TypeOffered,
-		Status:             corev1.ConditionFalse,
-		LastTransitionTime: metav1.Now(),
-		Reason:             ReasonTerminatingClaim,
-	}
-}
diff --git a/apis/apiextensions/v2alpha1/doc.go b/apis/apiextensions/v2alpha1/doc.go
index cdb801e0f..1c6d64665 100644
--- a/apis/apiextensions/v2alpha1/doc.go
+++ b/apis/apiextensions/v2alpha1/doc.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Crossplane Authors.
+Copyright 2025 The Crossplane Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/apis/apiextensions/v2alpha1/register.go b/apis/apiextensions/v2alpha1/register.go
index 56385b1fd..ab8791349 100644
--- a/apis/apiextensions/v2alpha1/register.go
+++ b/apis/apiextensions/v2alpha1/register.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2020 The Crossplane Authors.
+Copyright 2025 The Crossplane Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -42,11 +42,6 @@ var (
 
 // CompositeResourceDefinition type metadata.
 var (
-	CompositeResourceDefinitionKind             = reflect.TypeOf(CompositeResourceDefinition{}).Name()
-	CompositeResourceDefinitionGroupKind        = schema.GroupKind{Group: Group, Kind: CompositeResourceDefinitionKind}.String()
-	CompositeResourceDefinitionKindAPIVersion   = CompositeResourceDefinitionKind + "." + SchemeGroupVersion.String()
-	CompositeResourceDefinitionGroupVersionKind = SchemeGroupVersion.WithKind(CompositeResourceDefinitionKind)
-
 	ManagedResourceDefinitionKind             = reflect.TypeOf(ManagedResourceDefinition{}).Name()
 	ManagedResourceDefinitionGroupKind        = schema.GroupKind{Group: Group, Kind: ManagedResourceDefinitionKind}.String()
 	ManagedResourceDefinitionKindAPIVersion   = ManagedResourceDefinitionKind + "." + SchemeGroupVersion.String()
@@ -60,7 +55,6 @@ var (
 
 func init() {
 	SchemeBuilder.Register(
-		&CompositeResourceDefinition{}, &CompositeResourceDefinitionList{},
 		&ManagedResourceDefinition{}, &ManagedResourceDefinitionList{},
 		&ManagedResourceActivationPolicy{}, &ManagedResourceActivationPolicyList{},
 	)
diff --git a/apis/apiextensions/v2alpha1/zz_generated.deepcopy.go b/apis/apiextensions/v2alpha1/zz_generated.deepcopy.go
index 90533d151..ae9a5d25d 100644
--- a/apis/apiextensions/v2alpha1/zz_generated.deepcopy.go
+++ b/apis/apiextensions/v2alpha1/zz_generated.deepcopy.go
@@ -21,262 +21,10 @@ limitations under the License.
 package v2alpha1
 
 import (
-	commonv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
 	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinition) DeepCopyInto(out *CompositeResourceDefinition) {
-	*out = *in
-	out.TypeMeta = in.TypeMeta
-	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
-	in.Spec.DeepCopyInto(&out.Spec)
-	in.Status.DeepCopyInto(&out.Status)
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinition.
-func (in *CompositeResourceDefinition) DeepCopy() *CompositeResourceDefinition {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinition)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CompositeResourceDefinition) DeepCopyObject() runtime.Object {
-	if c := in.DeepCopy(); c != nil {
-		return c
-	}
-	return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionControllerStatus) DeepCopyInto(out *CompositeResourceDefinitionControllerStatus) {
-	*out = *in
-	out.CompositeResourceTypeRef = in.CompositeResourceTypeRef
-	out.CompositeResourceClaimTypeRef = in.CompositeResourceClaimTypeRef
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionControllerStatus.
-func (in *CompositeResourceDefinitionControllerStatus) DeepCopy() *CompositeResourceDefinitionControllerStatus {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionControllerStatus)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionList) DeepCopyInto(out *CompositeResourceDefinitionList) {
-	*out = *in
-	out.TypeMeta = in.TypeMeta
-	in.ListMeta.DeepCopyInto(&out.ListMeta)
-	if in.Items != nil {
-		in, out := &in.Items, &out.Items
-		*out = make([]CompositeResourceDefinition, len(*in))
-		for i := range *in {
-			(*in)[i].DeepCopyInto(&(*out)[i])
-		}
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionList.
-func (in *CompositeResourceDefinitionList) DeepCopy() *CompositeResourceDefinitionList {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionList)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CompositeResourceDefinitionList) DeepCopyObject() runtime.Object {
-	if c := in.DeepCopy(); c != nil {
-		return c
-	}
-	return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionSpec) DeepCopyInto(out *CompositeResourceDefinitionSpec) {
-	*out = *in
-	in.Names.DeepCopyInto(&out.Names)
-	if in.DefaultCompositionRef != nil {
-		in, out := &in.DefaultCompositionRef, &out.DefaultCompositionRef
-		*out = new(CompositionReference)
-		**out = **in
-	}
-	if in.EnforcedCompositionRef != nil {
-		in, out := &in.EnforcedCompositionRef, &out.EnforcedCompositionRef
-		*out = new(CompositionReference)
-		**out = **in
-	}
-	if in.DefaultCompositionUpdatePolicy != nil {
-		in, out := &in.DefaultCompositionUpdatePolicy, &out.DefaultCompositionUpdatePolicy
-		*out = new(commonv1.UpdatePolicy)
-		**out = **in
-	}
-	if in.Versions != nil {
-		in, out := &in.Versions, &out.Versions
-		*out = make([]CompositeResourceDefinitionVersion, len(*in))
-		for i := range *in {
-			(*in)[i].DeepCopyInto(&(*out)[i])
-		}
-	}
-	if in.Conversion != nil {
-		in, out := &in.Conversion, &out.Conversion
-		*out = new(v1.CustomResourceConversion)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.Metadata != nil {
-		in, out := &in.Metadata, &out.Metadata
-		*out = new(CompositeResourceDefinitionSpecMetadata)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.ClaimNames != nil {
-		in, out := &in.ClaimNames, &out.ClaimNames
-		*out = new(v1.CustomResourceDefinitionNames)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.DefaultCompositeDeletePolicy != nil {
-		in, out := &in.DefaultCompositeDeletePolicy, &out.DefaultCompositeDeletePolicy
-		*out = new(commonv1.CompositeDeletePolicy)
-		**out = **in
-	}
-	if in.ConnectionSecretKeys != nil {
-		in, out := &in.ConnectionSecretKeys, &out.ConnectionSecretKeys
-		*out = make([]string, len(*in))
-		copy(*out, *in)
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionSpec.
-func (in *CompositeResourceDefinitionSpec) DeepCopy() *CompositeResourceDefinitionSpec {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionSpec)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionSpecMetadata) DeepCopyInto(out *CompositeResourceDefinitionSpecMetadata) {
-	*out = *in
-	if in.Labels != nil {
-		in, out := &in.Labels, &out.Labels
-		*out = make(map[string]string, len(*in))
-		for key, val := range *in {
-			(*out)[key] = val
-		}
-	}
-	if in.Annotations != nil {
-		in, out := &in.Annotations, &out.Annotations
-		*out = make(map[string]string, len(*in))
-		for key, val := range *in {
-			(*out)[key] = val
-		}
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionSpecMetadata.
-func (in *CompositeResourceDefinitionSpecMetadata) DeepCopy() *CompositeResourceDefinitionSpecMetadata {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionSpecMetadata)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionStatus) DeepCopyInto(out *CompositeResourceDefinitionStatus) {
-	*out = *in
-	in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
-	out.Controllers = in.Controllers
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionStatus.
-func (in *CompositeResourceDefinitionStatus) DeepCopy() *CompositeResourceDefinitionStatus {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionStatus)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceDefinitionVersion) DeepCopyInto(out *CompositeResourceDefinitionVersion) {
-	*out = *in
-	if in.Deprecated != nil {
-		in, out := &in.Deprecated, &out.Deprecated
-		*out = new(bool)
-		**out = **in
-	}
-	if in.DeprecationWarning != nil {
-		in, out := &in.DeprecationWarning, &out.DeprecationWarning
-		*out = new(string)
-		**out = **in
-	}
-	if in.Schema != nil {
-		in, out := &in.Schema, &out.Schema
-		*out = new(CompositeResourceValidation)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.AdditionalPrinterColumns != nil {
-		in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
-		*out = make([]v1.CustomResourceColumnDefinition, len(*in))
-		copy(*out, *in)
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceDefinitionVersion.
-func (in *CompositeResourceDefinitionVersion) DeepCopy() *CompositeResourceDefinitionVersion {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceDefinitionVersion)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositeResourceValidation) DeepCopyInto(out *CompositeResourceValidation) {
-	*out = *in
-	in.OpenAPIV3Schema.DeepCopyInto(&out.OpenAPIV3Schema)
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeResourceValidation.
-func (in *CompositeResourceValidation) DeepCopy() *CompositeResourceValidation {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositeResourceValidation)
-	in.DeepCopyInto(out)
-	return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CompositionReference) DeepCopyInto(out *CompositionReference) {
-	*out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositionReference.
-func (in *CompositionReference) DeepCopy() *CompositionReference {
-	if in == nil {
-		return nil
-	}
-	out := new(CompositionReference)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ConnectionDetail) DeepCopyInto(out *ConnectionDetail) {
 	*out = *in
@@ -571,18 +319,3 @@ func (in *ManagedResourceDefinitionStatus) DeepCopy() *ManagedResourceDefinition
 	in.DeepCopyInto(out)
 	return out
 }
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *TypeReference) DeepCopyInto(out *TypeReference) {
-	*out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeReference.
-func (in *TypeReference) DeepCopy() *TypeReference {
-	if in == nil {
-		return nil
-	}
-	out := new(TypeReference)
-	in.DeepCopyInto(out)
-	return out
-}
diff --git a/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml b/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml
index 8d567b8f4..004336a2f 100644
--- a/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml
+++ b/cluster/crds/apiextensions.crossplane.io_compositeresourcedefinitions.yaml
@@ -29,6 +29,9 @@ spec:
     - jsonPath: .metadata.creationTimestamp
       name: AGE
       type: date
+    deprecated: true
+    deprecationWarning: CompositeResourceDefinition v1 is deprecated and will be removed
+      in a future release; consider migrating to v2
     name: v1
     schema:
       openAPIV3Schema:
@@ -617,7 +620,7 @@ spec:
     - jsonPath: .metadata.creationTimestamp
       name: AGE
       type: date
-    name: v2alpha1
+    name: v2
     schema:
       openAPIV3Schema:
         description: |-
diff --git a/generate.go b/generate.go
index 79e246515..aeb350400 100644
--- a/generate.go
+++ b/generate.go
@@ -46,7 +46,7 @@ limitations under the License.
 
 // Generate deepcopy methodsets and CRD manifests
 //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/pkg/v1beta1;./apis/pkg/v1 crd:crdVersions=v1,generateEmbeddedObjectMeta=true output:artifacts:config=./cluster/crds
-//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/apiextensions/v1alpha1;./apis/apiextensions/v1beta1;./apis/apiextensions/v1;./apis/apiextensions/v2alpha1 crd:crdVersions=v1 output:artifacts:config=./cluster/crds
+//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/apiextensions/v1alpha1;./apis/apiextensions/v1beta1;./apis/apiextensions/v1;./apis/apiextensions/v2alpha1;./apis/apiextensions/v2 crd:crdVersions=v1 output:artifacts:config=./cluster/crds
 //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/protection/v1beta1 crd:crdVersions=v1 output:artifacts:config=./cluster/crds
 //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=./hack/boilerplate.go.txt paths=./apis/ops/v1alpha1 crd:crdVersions=v1 output:artifacts:config=./cluster/crds
 
diff --git a/internal/xpkg/lint.go b/internal/xpkg/lint.go
index 6fd90ea76..fd0ddb4d2 100644
--- a/internal/xpkg/lint.go
+++ b/internal/xpkg/lint.go
@@ -27,7 +27,7 @@ import (
 	"github.com/crossplane/crossplane-runtime/pkg/parser"
 
 	v1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
-	"github.com/crossplane/crossplane/apis/apiextensions/v2alpha1"
+	v2 "github.com/crossplane/crossplane/apis/apiextensions/v2"
 	"github.com/crossplane/crossplane/apis/ops/v1alpha1"
 	pkgmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
 	"github.com/crossplane/crossplane/internal/version"
@@ -196,7 +196,7 @@ func IsValidatingWebhookConfiguration(o runtime.Object) error {
 // IsXRD checks that an object is a CompositeResourceDefinition.
 func IsXRD(o runtime.Object) error {
 	switch o.(type) {
-	case *v1.CompositeResourceDefinition, *v2alpha1.CompositeResourceDefinition:
+	case *v1.CompositeResourceDefinition, *v2.CompositeResourceDefinition:
 		return nil
 	default:
 		return errors.New(errNotXRD)
diff --git a/internal/xpkg/scheme.go b/internal/xpkg/scheme.go
index d43f95961..359782e57 100644
--- a/internal/xpkg/scheme.go
+++ b/internal/xpkg/scheme.go
@@ -24,6 +24,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/conversion"
 
 	v1 "github.com/crossplane/crossplane/apis/apiextensions/v1"
+	v2 "github.com/crossplane/crossplane/apis/apiextensions/v2"
 	"github.com/crossplane/crossplane/apis/apiextensions/v2alpha1"
 	opsv1alpha1 "github.com/crossplane/crossplane/apis/ops/v1alpha1"
 	pkgmetav1 "github.com/crossplane/crossplane/apis/pkg/meta/v1"
@@ -66,6 +67,10 @@ func BuildObjectScheme() (*runtime.Scheme, error) {
 		return nil, err
 	}
 
+	if err := v2.AddToScheme(objScheme); err != nil {
+		return nil, err
+	}
+
 	if err := extv1beta1.AddToScheme(objScheme); err != nil {
 		return nil, err
 	}



Test patch:
diff --git a/test/e2e/install_test.go b/test/e2e/install_test.go
index 0fdbc391f..f4f80d2e3 100644
--- a/test/e2e/install_test.go
+++ b/test/e2e/install_test.go
@@ -184,7 +184,14 @@ func TestCrossplaneLifecycle(t *testing.T) {
 				}))),
 			)).
 			WithTeardown("DeletePrerequisites", funcs.AllOf(
-				funcs.DeleteResourcesWithPropagationPolicy(manifests, "setup/*.yaml", metav1.DeletePropagationForeground),
+				// TODO(lsviben): We are deleting the XRD without foreground deletion as it
+				// messes up the deletion, when the XRD version downgrades from v2 to v1. For
+				// some reason, the XRD deletion gets stuck, as the garbage collector is still
+				// trying to delete the XRD with v2 version, which is not available anymore.
+				funcs.DeleteResources(manifests, "setup/definition.yaml"),
+				funcs.DeleteResourcesWithPropagationPolicy(manifests, "setup/composition.yaml", metav1.DeletePropagationForeground),
+				funcs.DeleteResourcesWithPropagationPolicy(manifests, "setup/functions.yaml", metav1.DeletePropagationForeground),
+				funcs.DeleteResourcesWithPropagationPolicy(manifests, "setup/provider.yaml", metav1.DeletePropagationForeground),
 				funcs.ResourcesDeletedWithin(3*time.Minute, manifests, "setup/*.yaml"),
 
 				// TODO(negz): We're seeing revisions sticking
diff --git a/test/e2e/manifests/apiextensions/composition/basic-cluster/setup/definition.yaml b/test/e2e/manifests/apiextensions/composition/basic-cluster/setup/definition.yaml
index 75ddbc4ad..ffddfd854 100644
--- a/test/e2e/manifests/apiextensions/composition/basic-cluster/setup/definition.yaml
+++ b/test/e2e/manifests/apiextensions/composition/basic-cluster/setup/definition.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.crossplane.io/v2alpha1
+apiVersion: apiextensions.crossplane.io/v2
 kind: CompositeResourceDefinition
 metadata:
   name: clustertests.example.org
diff --git a/test/e2e/manifests/apiextensions/composition/basic-namespaced/setup/definition.yaml b/test/e2e/manifests/apiextensions/composition/basic-namespaced/setup/definition.yaml
index aba3e0e81..9d909d6b6 100644
--- a/test/e2e/manifests/apiextensions/composition/basic-namespaced/setup/definition.yaml
+++ b/test/e2e/manifests/apiextensions/composition/basic-namespaced/setup/definition.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.crossplane.io/v2alpha1
+apiVersion: apiextensions.crossplane.io/v2
 kind: CompositeResourceDefinition
 metadata:
   name: tests.example.org
diff --git a/test/e2e/manifests/apiextensions/composition/namespaced-xr-no-cluster-scoped-resource/setup/definition.yaml b/test/e2e/manifests/apiextensions/composition/namespaced-xr-no-cluster-scoped-resource/setup/definition.yaml
index 109eaeebf..0df1208bb 100644
--- a/test/e2e/manifests/apiextensions/composition/namespaced-xr-no-cluster-scoped-resource/setup/definition.yaml
+++ b/test/e2e/manifests/apiextensions/composition/namespaced-xr-no-cluster-scoped-resource/setup/definition.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.crossplane.io/v2alpha1
+apiVersion: apiextensions.crossplane.io/v2
 kind: CompositeResourceDefinition
 metadata:
   name: namespacedtests.validation.example.org
diff --git a/test/e2e/manifests/pkg/deployment-runtime-config/setup/definition.yaml b/test/e2e/manifests/pkg/deployment-runtime-config/setup/definition.yaml
index aba3e0e81..9d909d6b6 100644
--- a/test/e2e/manifests/pkg/deployment-runtime-config/setup/definition.yaml
+++ b/test/e2e/manifests/pkg/deployment-runtime-config/setup/definition.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.crossplane.io/v2alpha1
+apiVersion: apiextensions.crossplane.io/v2
 kind: CompositeResourceDefinition
 metadata:
   name: tests.example.org
diff --git a/test/e2e/manifests/pkg/externally-managed-service-account/setup/definition.yaml b/test/e2e/manifests/pkg/externally-managed-service-account/setup/definition.yaml
index aba3e0e81..9d909d6b6 100644
--- a/test/e2e/manifests/pkg/externally-managed-service-account/setup/definition.yaml
+++ b/test/e2e/manifests/pkg/externally-managed-service-account/setup/definition.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.crossplane.io/v2alpha1
+apiVersion: apiextensions.crossplane.io/v2
 kind: CompositeResourceDefinition
 metadata:
   name: tests.example.org