Repository: GoogleCloudPlatform/k8s-config-connector
Pull request #3985: feat: add types, mappers and fuzzer for BackupDRBackupPlan 
Author: login: jingyih
Labels: name: lgtm
name: approved
Created: 2025-03-12T23:09:13Z | Merged: 2025-03-13T20:53:32Z



Diff:
diff --git a/apis/backupdr/v1alpha1/backupplan_identity.go b/apis/backupdr/v1alpha1/backupplan_identity.go
new file mode 100644
index 00000000000..9278cdf400d
--- /dev/null
+++ b/apis/backupdr/v1alpha1/backupplan_identity.go
@@ -0,0 +1,117 @@
+// Copyright 2025 Google LLC
+//
+// 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 v1alpha1
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common"
+	refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+// BackupPlanIdentity defines the resource reference to BackupDRBackupPlan, which "External" field
+// holds the GCP identifier for the KRM object.
+type BackupPlanIdentity struct {
+	parent *BackupPlanParent
+	id     string
+}
+
+func (i *BackupPlanIdentity) String() string {
+	return i.parent.String() + "/backupPlans/" + i.id
+}
+
+func (i *BackupPlanIdentity) ID() string {
+	return i.id
+}
+
+func (i *BackupPlanIdentity) Parent() *BackupPlanParent {
+	return i.parent
+}
+
+type BackupPlanParent struct {
+	ProjectID string
+	Location  string
+}
+
+func (p *BackupPlanParent) String() string {
+	return "projects/" + p.ProjectID + "/locations/" + p.Location
+}
+
+// New builds a BackupPlanIdentity from the Config Connector BackupPlan object.
+func NewBackupPlanIdentity(ctx context.Context, reader client.Reader, obj *BackupDRBackupPlan) (*BackupPlanIdentity, error) {
+
+	// Get Parent
+	projectRef, err := refsv1beta1.ResolveProject(ctx, reader, obj.GetNamespace(), obj.Spec.ProjectRef)
+	if err != nil {
+		return nil, err
+	}
+	projectID := projectRef.ProjectID
+	if projectID == "" {
+		return nil, fmt.Errorf("cannot resolve project")
+	}
+	location := obj.Spec.Location
+
+	// Get desired ID
+	resourceID := common.ValueOf(obj.Spec.ResourceID)
+	if resourceID == "" {
+		resourceID = obj.GetName()
+	}
+	if resourceID == "" {
+		return nil, fmt.Errorf("cannot resolve resource ID")
+	}
+
+	// Use approved External
+	externalRef := common.ValueOf(obj.Status.ExternalRef)
+	if externalRef != "" {
+		// Validate desired with actual
+		actualParent, actualResourceID, err := ParseBackupPlanExternal(externalRef)
+		if err != nil {
+			return nil, err
+		}
+		if actualParent.ProjectID != projectID {
+			return nil, fmt.Errorf("spec.projectRef changed, expect %s, got %s", actualParent.ProjectID, projectID)
+		}
+		if actualParent.Location != location {
+			return nil, fmt.Errorf("spec.location changed, expect %s, got %s", actualParent.Location, location)
+		}
+		if actualResourceID != resourceID {
+			return nil, fmt.Errorf("cannot reset `metadata.name` or `spec.resourceID` to %s, since it has already assigned to %s",
+				resourceID, actualResourceID)
+		}
+	}
+	return &BackupPlanIdentity{
+		parent: &BackupPlanParent{
+			ProjectID: projectID,
+			Location:  location,
+		},
+		id: resourceID,
+	}, nil
+}
+
+func ParseBackupPlanExternal(external string) (parent *BackupPlanParent, resourceID string, err error) {
+	tokens := strings.Split(external, "/")
+	if len(tokens) != 6 || tokens[0] != "projects" || tokens[2] != "locations" || tokens[4] != "backupPlans" {
+		return nil, "", fmt.Errorf("format of BackupDRBackupPlan external=%q was not known (use projects/{{projectID}}/locations/{{location}}/backupPlans/{{backupplanID}})", external)
+	}
+	parent = &BackupPlanParent{
+		ProjectID: tokens[1],
+		Location:  tokens[3],
+	}
+	resourceID = tokens[5]
+	return parent, resourceID, nil
+}
diff --git a/apis/backupdr/v1alpha1/backupplan_reference.go b/apis/backupdr/v1alpha1/backupplan_reference.go
new file mode 100644
index 00000000000..934222028d7
--- /dev/null
+++ b/apis/backupdr/v1alpha1/backupplan_reference.go
@@ -0,0 +1,83 @@
+// Copyright 2025 Google LLC
+//
+// 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 v1alpha1
+
+import (
+	"context"
+	"fmt"
+
+	refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
+	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+var _ refsv1beta1.ExternalNormalizer = &BackupPlanRef{}
+
+// BackupPlanRef defines the resource reference to BackupDRBackupPlan, which "External" field
+// holds the GCP identifier for the KRM object.
+type BackupPlanRef struct {
+	// A reference to an externally managed BackupDRBackupPlan resource.
+	// Should be in the format "projects/{{projectID}}/locations/{{location}}/backupplans/{{backupplanID}}".
+	External string `json:"external,omitempty"`
+
+	// The name of a BackupDRBackupPlan resource.
+	Name string `json:"name,omitempty"`
+
+	// The namespace of a BackupDRBackupPlan resource.
+	Namespace string `json:"namespace,omitempty"`
+}
+
+// NormalizedExternal provision the "External" value for other resource that depends on BackupDRBackupPlan.
+// If the "External" is given in the other resource's spec.BackupDRBackupPlanRef, the given value will be used.
+// Otherwise, the "Name" and "Namespace" will be used to query the actual BackupDRBackupPlan object from the cluster.
+func (r *BackupPlanRef) NormalizedExternal(ctx context.Context, reader client.Reader, otherNamespace string) (string, error) {
+	if r.External != "" && r.Name != "" {
+		return "", fmt.Errorf("cannot specify both name and external on %s reference", BackupDRBackupPlanGVK.Kind)
+	}
+	// From given External
+	if r.External != "" {
+		if _, _, err := ParseBackupPlanExternal(r.External); err != nil {
+			return "", err
+		}
+		return r.External, nil
+	}
+
+	// From the Config Connector object
+	if r.Namespace == "" {
+		r.Namespace = otherNamespace
+	}
+	key := types.NamespacedName{Name: r.Name, Namespace: r.Namespace}
+	u := &unstructured.Unstructured{}
+	u.SetGroupVersionKind(BackupDRBackupPlanGVK)
+	if err := reader.Get(ctx, key, u); err != nil {
+		if apierrors.IsNotFound(err) {
+			return "", k8s.NewReferenceNotFoundError(u.GroupVersionKind(), key)
+		}
+		return "", fmt.Errorf("reading referenced %s %s: %w", BackupDRBackupPlanGVK, key, err)
+	}
+	// Get external from status.externalRef. This is the most trustworthy place.
+	actualExternalRef, _, err := unstructured.NestedString(u.Object, "status", "externalRef")
+	if err != nil {
+		return "", fmt.Errorf("reading status.externalRef: %w", err)
+	}
+	if actualExternalRef == "" {
+		return "", k8s.NewReferenceNotReadyError(u.GroupVersionKind(), key)
+	}
+	r.External = actualExternalRef
+	return r.External, nil
+}
diff --git a/apis/backupdr/v1alpha1/backupplan_types.go b/apis/backupdr/v1alpha1/backupplan_types.go
new file mode 100644
index 00000000000..f17c74ba4a3
--- /dev/null
+++ b/apis/backupdr/v1alpha1/backupplan_types.go
@@ -0,0 +1,146 @@
+// Copyright 2025 Google LLC
+//
+// 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 v1alpha1
+
+import (
+	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/k8s/v1alpha1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var BackupDRBackupPlanGVK = GroupVersion.WithKind("BackupDRBackupPlan")
+
+// BackupDRBackupPlanSpec defines the desired state of BackupDRBackupPlan
+// +kcc:proto=google.cloud.backupdr.v1.BackupPlan
+type BackupDRBackupPlanSpec struct {
+	// The BackupDRBackupPlan name. If not given, the metadata.name will be used.
+	ResourceID *string `json:"resourceID,omitempty"`
+
+	Parent `json:",inline"`
+
+	// Optional. The description of the `BackupPlan` resource.
+	//
+	//  The description allows for additional details about `BackupPlan` and its
+	//  use cases to be provided. An example description is the following:  "This
+	//  is a backup plan that performs a daily backup at 6pm and retains data for 3
+	//  months". The description must be at most 2048 characters.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.description
+	Description *string `json:"description,omitempty"`
+
+	// Optional. This collection of key/value pairs allows for custom labels to be
+	//  supplied by the user.  Example, {"tag": "Weekly"}.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.labels
+	Labels map[string]string `json:"labels,omitempty"`
+
+	// Required. The backup rules for this `BackupPlan`. There must be at least
+	//  one `BackupRule` message.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.backup_rules
+	BackupRules []BackupRule `json:"backupRules,omitempty"`
+
+	// Required. The resource type to which the `BackupPlan` will be applied.
+	//  Examples include, "compute.googleapis.com/Instance",
+	//  "sqladmin.googleapis.com/Instance", or "alloydb.googleapis.com/Cluster".
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.resource_type
+	ResourceType *string `json:"resourceType,omitempty"`
+
+	// Optional. `etag` is returned from the service in the response. As a user of
+	//  the service, you may provide an etag value in this field to prevent stale
+	//  resources.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.etag
+	Etag *string `json:"etag,omitempty"`
+
+	// Required. Resource name of backup vault which will be used as storage
+	//  location for backups. Format:
+	//  projects/{project}/locations/{location}/backupVaults/{backupvault}
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.backup_vault
+	BackupVault *string `json:"backupVault,omitempty"` // TODO: add BackupDRBackupVault and make this field a reference field.
+}
+
+// BackupDRBackupPlanStatus defines the config connector machine state of BackupDRBackupPlan
+type BackupDRBackupPlanStatus struct {
+	/* Conditions represent the latest available observations of the
+	   object's current state. */
+	Conditions []v1alpha1.Condition `json:"conditions,omitempty"`
+
+	// ObservedGeneration is the generation of the resource that was most recently observed by the Config Connector controller. If this is equal to metadata.generation, then that means that the current reported status reflects the most recent desired state of the resource.
+	ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
+
+	// A unique specifier for the BackupDRBackupPlan resource in GCP.
+	ExternalRef *string `json:"externalRef,omitempty"`
+
+	// ObservedState is the state of the resource as most recently observed in GCP.
+	ObservedState *BackupDRBackupPlanObservedState `json:"observedState,omitempty"`
+}
+
+// BackupDRBackupPlanObservedState is the state of the BackupDRBackupPlan resource as most recently observed in GCP.
+// +kcc:proto=google.cloud.backupdr.v1.BackupPlan
+type BackupDRBackupPlanObservedState struct {
+	// Output only. Identifier. The resource name of the `BackupPlan`.
+	//
+	//  Format: `projects/{project}/locations/{location}/backupPlans/{backup_plan}`
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.name
+	// NOTYET: this field serves the same purpose as externalRef
+	// Name *string `json:"name,omitempty"`
+
+	// Output only. When the `BackupPlan` was created.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.create_time
+	CreateTime *string `json:"createTime,omitempty"`
+
+	// Output only. When the `BackupPlan` was last updated.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.update_time
+	UpdateTime *string `json:"updateTime,omitempty"`
+
+	// Output only. The `State` for the `BackupPlan`.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.state
+	State *string `json:"state,omitempty"`
+
+	// Output only. The Google Cloud Platform Service Account to be used by the
+	//  BackupVault for taking backups. Specify the email address of the Backup
+	//  Vault Service Account.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupPlan.backup_vault_service_account
+	BackupVaultServiceAccount *string `json:"backupVaultServiceAccount,omitempty"`
+}
+
+// +genclient
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// +kubebuilder:resource:categories=gcp,shortName=gcpbackupdrbackupplan;gcpbackupdrbackupplans
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="cnrm.cloud.google.com/managed-by-kcc=true";"cnrm.cloud.google.com/system=true"
+// +kubebuilder:printcolumn:name="Age",JSONPath=".metadata.creationTimestamp",type="date"
+// +kubebuilder:printcolumn:name="Ready",JSONPath=".status.conditions[?(@.type=='Ready')].status",type="string",description="When 'True', the most recent reconcile of the resource succeeded"
+// +kubebuilder:printcolumn:name="Status",JSONPath=".status.conditions[?(@.type=='Ready')].reason",type="string",description="The reason for the value in 'Ready'"
+// +kubebuilder:printcolumn:name="Status Age",JSONPath=".status.conditions[?(@.type=='Ready')].lastTransitionTime",type="date",description="The last transition time for the value in 'Status'"
+
+// BackupDRBackupPlan is the Schema for the BackupDRBackupPlan API
+// +k8s:openapi-gen=true
+type BackupDRBackupPlan struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// +required
+	Spec   BackupDRBackupPlanSpec   `json:"spec,omitempty"`
+	Status BackupDRBackupPlanStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+// BackupDRBackupPlanList contains a list of BackupDRBackupPlan
+type BackupDRBackupPlanList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []BackupDRBackupPlan `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&BackupDRBackupPlan{}, &BackupDRBackupPlanList{})
+}
diff --git a/apis/backupdr/v1alpha1/generate.sh b/apis/backupdr/v1alpha1/generate.sh
index 259f016e3a9..270b5424c40 100755
--- a/apis/backupdr/v1alpha1/generate.sh
+++ b/apis/backupdr/v1alpha1/generate.sh
@@ -21,13 +21,19 @@ set -o pipefail
 REPO_ROOT="$(git rev-parse --show-toplevel)"
 cd ${REPO_ROOT}/dev/tools/controllerbuilder
 
-go run . generate-types     --service google.cloud.backupdr.v1     --api-version backupdr.cnrm.cloud.google.com/v1alpha1     --resource BackupDRManagementServer:ManagementServer
+go run . generate-types \
+    --service google.cloud.backupdr.v1 \
+    --api-version backupdr.cnrm.cloud.google.com/v1alpha1 \
+    --resource BackupDRManagementServer:ManagementServer \
+    --resource BackupDRBackupPlan:BackupPlan
 
-go run . generate-mapper     --service google.cloud.backupdr.v1     --api-version backupdr.cnrm.cloud.google.com/v1alpha1
+go run . generate-mapper \
+    --service google.cloud.backupdr.v1 \
+    --api-version backupdr.cnrm.cloud.google.com/v1alpha1
 
 
 cd ${REPO_ROOT}
 dev/tasks/generate-crds
 
-go run -mod=readonly golang.org/x/tools/cmd/goimports@latest -w  pkg/controller/direct/backupdr/
+${REPO_ROOT}/dev/tasks/fix-gofmt
 
diff --git a/apis/backupdr/v1alpha1/types.generated.go b/apis/backupdr/v1alpha1/types.generated.go
index 123c762df08..f01e2795ab4 100644
--- a/apis/backupdr/v1alpha1/types.generated.go
+++ b/apis/backupdr/v1alpha1/types.generated.go
@@ -14,16 +14,138 @@
 
 package v1alpha1
 
-// +kcc:proto=google.cloud.backupdr.v1.ManagementURI
-type ManagementURI struct {
+// +kcc:proto=google.cloud.backupdr.v1.BackupRule
+type BackupRule struct {
+	// Required. Immutable. The unique id of this `BackupRule`. The `rule_id` is
+	//  unique per `BackupPlan`.The `rule_id` must start with a lowercase letter
+	//  followed by up to 62 lowercase letters, numbers, or hyphens. Pattern,
+	//  /[a-z][a-z0-9-]{,62}/.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupRule.rule_id
+	RuleID *string `json:"ruleID,omitempty"`
+
+	// Required. Configures the duration for which backup data will be kept. It is
+	//  defined in “days”. The value should be greater than or equal to minimum
+	//  enforced retention of the backup vault.
+	//
+	//  Minimum value is 1 and maximum value is 90 for hourly backups.
+	//  Minimum value is 1 and maximum value is 90 for daily backups.
+	//  Minimum value is 7 and maximum value is 186 for weekly backups.
+	//  Minimum value is 30 and maximum value is 732 for monthly backups.
+	//  Minimum value is 365 and maximum value is 36159 for yearly backups.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupRule.backup_retention_days
+	BackupRetentionDays *int32 `json:"backupRetentionDays,omitempty"`
+
+	// Required. Defines a schedule that runs within the confines of a defined
+	//  window of time.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupRule.standard_schedule
+	StandardSchedule *StandardSchedule `json:"standardSchedule,omitempty"`
 }
 
-// +kcc:proto=google.cloud.backupdr.v1.WorkforceIdentityBasedManagementURI
-type WorkforceIdentityBasedManagementURI struct {
+// +kcc:proto=google.cloud.backupdr.v1.BackupWindow
+type BackupWindow struct {
+	// Required. The hour of day (0-23) when the window starts for e.g. if value
+	//  of start hour of day is 6 that mean backup window start at 6:00.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupWindow.start_hour_of_day
+	StartHourOfDay *int32 `json:"startHourOfDay,omitempty"`
+
+	// Required. The hour of day (1-24) when the window end for e.g. if value of
+	//  end hour of day is 10 that mean backup window end time is 10:00.
+	//
+	//  End hour of day should be greater than start hour of day.
+	//  0 <= start_hour_of_day < end_hour_of_day <= 24
+	//
+	//  End hour of day is not include in backup window that mean if
+	//  end_hour_of_day= 10 jobs should start before 10:00.
+	// +kcc:proto:field=google.cloud.backupdr.v1.BackupWindow.end_hour_of_day
+	EndHourOfDay *int32 `json:"endHourOfDay,omitempty"`
 }
 
-// +kcc:proto=google.cloud.backupdr.v1.WorkforceIdentityBasedOAuth2ClientID
-type WorkforceIdentityBasedOAuth2ClientID struct {
+// +kcc:proto=google.cloud.backupdr.v1.StandardSchedule
+type StandardSchedule struct {
+	// Required. Specifies the `RecurrenceType` for the schedule.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.recurrence_type
+	RecurrenceType *string `json:"recurrenceType,omitempty"`
+
+	// Optional. Specifies frequency for hourly backups. A hourly frequency of 2
+	//  means jobs will run every 2 hours from start time till end time defined.
+	//
+	//  This is required for `recurrence_type`, `HOURLY` and is not applicable
+	//  otherwise. A validation error will occur if a value is supplied and
+	//  `recurrence_type` is not `HOURLY`.
+	//
+	//  Value of hourly frequency should be between 6 and 23.
+	//
+	//  Reason for limit : We found that there is bandwidth limitation of 3GB/S for
+	//  GMI while taking a backup and 5GB/S while doing a restore. Given the amount
+	//  of parallel backups and restore we are targeting, this will potentially
+	//  take the backup time to mins and hours (in worst case scenario).
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.hourly_frequency
+	HourlyFrequency *int32 `json:"hourlyFrequency,omitempty"`
+
+	// Optional. Specifies days of week like, MONDAY or TUESDAY, on which jobs
+	//  will run.
+	//
+	//  This is required for `recurrence_type`, `WEEKLY` and is not applicable
+	//  otherwise. A validation error will occur if a value is supplied and
+	//  `recurrence_type` is not `WEEKLY`.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.days_of_week
+	DaysOfWeek []string `json:"daysOfWeek,omitempty"`
+
+	// Optional. Specifies days of months like 1, 5, or 14 on which jobs will run.
+	//
+	//  Values for `days_of_month` are only applicable for `recurrence_type`,
+	//  `MONTHLY` and `YEARLY`. A validation error will occur if other values are
+	//  supplied.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.days_of_month
+	DaysOfMonth []int32 `json:"daysOfMonth,omitempty"`
+
+	// Optional. Specifies a week day of the month like, FIRST SUNDAY or LAST
+	//  MONDAY, on which jobs will run. This will be specified by two fields in
+	//  `WeekDayOfMonth`, one for the day, e.g. `MONDAY`, and one for the week,
+	//  e.g. `LAST`.
+	//
+	//  This field is only applicable for `recurrence_type`, `MONTHLY` and
+	//  `YEARLY`. A validation error will occur if other values are supplied.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.week_day_of_month
+	WeekDayOfMonth *WeekDayOfMonth `json:"weekDayOfMonth,omitempty"`
+
+	// Optional. Specifies the months of year, like `FEBRUARY` and/or `MAY`, on
+	//  which jobs will run.
+	//
+	//  This field is only applicable when `recurrence_type` is `YEARLY`. A
+	//  validation error will occur if other values are supplied.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.months
+	Months []string `json:"months,omitempty"`
+
+	// Required. A BackupWindow defines the window of day during which backup jobs
+	//  will run. Jobs are queued at the beginning of the window and will be marked
+	//  as `NOT_RUN` if they do not start by the end of the window.
+	//
+	//  Note: running jobs will not be cancelled at the end of the window.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.backup_window
+	BackupWindow *BackupWindow `json:"backupWindow,omitempty"`
+
+	// Required. The time zone to be used when interpreting the schedule.
+	//  The value of this field must be a time zone name from the IANA tz database.
+	//  See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for the
+	//  list of valid timezone names. For e.g., Europe/Paris.
+	// +kcc:proto:field=google.cloud.backupdr.v1.StandardSchedule.time_zone
+	TimeZone *string `json:"timeZone,omitempty"`
+}
+
+// +kcc:proto=google.cloud.backupdr.v1.WeekDayOfMonth
+type WeekDayOfMonth struct {
+	// Required. Specifies the week of the month.
+	// +kcc:proto:field=google.cloud.backupdr.v1.WeekDayOfMonth.week_of_month
+	WeekOfMonth *string `json:"weekOfMonth,omitempty"`
+
+	// Required. Specifies the day of the week.
+	// +kcc:proto:field=google.cloud.backupdr.v1.WeekDayOfMonth.day_of_week
+	DayOfWeek *string `json:"dayOfWeek,omitempty"`
+}
+
+// +kcc:proto=google.cloud.backupdr.v1.WorkforceIdentityBasedManagementURI
+type WorkforceIdentityBasedManagementURI struct {
 }
 
 // +kcc:proto=google.cloud.backupdr.v1.WorkforceIdentityBasedManagementURI
diff --git a/apis/backupdr/v1alpha1/zz_generated.deepcopy.go b/apis/backupdr/v1alpha1/zz_generated.deepcopy.go
index 1d1e2a6acad..dd1671a07de 100644
--- a/apis/backupdr/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/backupdr/v1alpha1/zz_generated.deepcopy.go
@@ -24,6 +24,190 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupDRBackupPlan) DeepCopyInto(out *BackupDRBackupPlan) {
+	*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 BackupDRBackupPlan.
+func (in *BackupDRBackupPlan) DeepCopy() *BackupDRBackupPlan {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupDRBackupPlan)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BackupDRBackupPlan) 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 *BackupDRBackupPlanList) DeepCopyInto(out *BackupDRBackupPlanList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]BackupDRBackupPlan, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupDRBackupPlanList.
+func (in *BackupDRBackupPlanList) DeepCopy() *BackupDRBackupPlanList {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupDRBackupPlanList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BackupDRBackupPlanList) 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 *BackupDRBackupPlanObservedState) DeepCopyInto(out *BackupDRBackupPlanObservedState) {
+	*out = *in
+	if in.CreateTime != nil {
+		in, out := &in.CreateTime, &out.CreateTime
+		*out = new(string)
+		**out = **in
+	}
+	if in.UpdateTime != nil {
+		in, out := &in.UpdateTime, &out.UpdateTime
+		*out = new(string)
+		**out = **in
+	}
+	if in.State != nil {
+		in, out := &in.State, &out.State
+		*out = new(string)
+		**out = **in
+	}
+	if in.BackupVaultServiceAccount != nil {
+		in, out := &in.BackupVaultServiceAccount, &out.BackupVaultServiceAccount
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupDRBackupPlanObservedState.
+func (in *BackupDRBackupPlanObservedState) DeepCopy() *BackupDRBackupPlanObservedState {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupDRBackupPlanObservedState)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupDRBackupPlanSpec) DeepCopyInto(out *BackupDRBackupPlanSpec) {
+	*out = *in
+	if in.ResourceID != nil {
+		in, out := &in.ResourceID, &out.ResourceID
+		*out = new(string)
+		**out = **in
+	}
+	in.Parent.DeepCopyInto(&out.Parent)
+	if in.Description != nil {
+		in, out := &in.Description, &out.Description
+		*out = new(string)
+		**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.BackupRules != nil {
+		in, out := &in.BackupRules, &out.BackupRules
+		*out = make([]BackupRule, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.ResourceType != nil {
+		in, out := &in.ResourceType, &out.ResourceType
+		*out = new(string)
+		**out = **in
+	}
+	if in.Etag != nil {
+		in, out := &in.Etag, &out.Etag
+		*out = new(string)
+		**out = **in
+	}
+	if in.BackupVault != nil {
+		in, out := &in.BackupVault, &out.BackupVault
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupDRBackupPlanSpec.
+func (in *BackupDRBackupPlanSpec) DeepCopy() *BackupDRBackupPlanSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupDRBackupPlanSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupDRBackupPlanStatus) DeepCopyInto(out *BackupDRBackupPlanStatus) {
+	*out = *in
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]k8sv1alpha1.Condition, len(*in))
+		copy(*out, *in)
+	}
+	if in.ObservedGeneration != nil {
+		in, out := &in.ObservedGeneration, &out.ObservedGeneration
+		*out = new(int64)
+		**out = **in
+	}
+	if in.ExternalRef != nil {
+		in, out := &in.ExternalRef, &out.ExternalRef
+		*out = new(string)
+		**out = **in
+	}
+	if in.ObservedState != nil {
+		in, out := &in.ObservedState, &out.ObservedState
+		*out = new(BackupDRBackupPlanObservedState)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupDRBackupPlanStatus.
+func (in *BackupDRBackupPlanStatus) DeepCopy() *BackupDRBackupPlanStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupDRBackupPlanStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BackupDRManagementServer) DeepCopyInto(out *BackupDRManagementServer) {
 	*out = *in
@@ -223,6 +407,111 @@ func (in *BackupDRManagementServerStatus) DeepCopy() *BackupDRManagementServerSt
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupPlanIdentity) DeepCopyInto(out *BackupPlanIdentity) {
+	*out = *in
+	if in.parent != nil {
+		in, out := &in.parent, &out.parent
+		*out = new(BackupPlanParent)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupPlanIdentity.
+func (in *BackupPlanIdentity) DeepCopy() *BackupPlanIdentity {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupPlanIdentity)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupPlanParent) DeepCopyInto(out *BackupPlanParent) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupPlanParent.
+func (in *BackupPlanParent) DeepCopy() *BackupPlanParent {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupPlanParent)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupPlanRef) DeepCopyInto(out *BackupPlanRef) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupPlanRef.
+func (in *BackupPlanRef) DeepCopy() *BackupPlanRef {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupPlanRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupRule) DeepCopyInto(out *BackupRule) {
+	*out = *in
+	if in.RuleID != nil {
+		in, out := &in.RuleID, &out.RuleID
+		*out = new(string)
+		**out = **in
+	}
+	if in.BackupRetentionDays != nil {
+		in, out := &in.BackupRetentionDays, &out.BackupRetentionDays
+		*out = new(int32)
+		**out = **in
+	}
+	if in.StandardSchedule != nil {
+		in, out := &in.StandardSchedule, &out.StandardSchedule
+		*out = new(StandardSchedule)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRule.
+func (in *BackupRule) DeepCopy() *BackupRule {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupRule)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackupWindow) DeepCopyInto(out *BackupWindow) {
+	*out = *in
+	if in.StartHourOfDay != nil {
+		in, out := &in.StartHourOfDay, &out.StartHourOfDay
+		*out = new(int32)
+		**out = **in
+	}
+	if in.EndHourOfDay != nil {
+		in, out := &in.EndHourOfDay, &out.EndHourOfDay
+		*out = new(int32)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupWindow.
+func (in *BackupWindow) DeepCopy() *BackupWindow {
+	if in == nil {
+		return nil
+	}
+	out := new(BackupWindow)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ManagementServerIdentity) DeepCopyInto(out *ManagementServerIdentity) {
 	*out = *in
@@ -273,21 +562,6 @@ func (in *ManagementServerRef) DeepCopy() *ManagementServerRef {
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *ManagementURI) DeepCopyInto(out *ManagementURI) {
-	*out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementURI.
-func (in *ManagementURI) DeepCopy() *ManagementURI {
-	if in == nil {
-		return nil
-	}
-	out := new(ManagementURI)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ManagementURIObservedState) DeepCopyInto(out *ManagementURIObservedState) {
 	*out = *in
@@ -358,6 +632,86 @@ func (in *Parent) DeepCopy() *Parent {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *StandardSchedule) DeepCopyInto(out *StandardSchedule) {
+	*out = *in
+	if in.RecurrenceType != nil {
+		in, out := &in.RecurrenceType, &out.RecurrenceType
+		*out = new(string)
+		**out = **in
+	}
+	if in.HourlyFrequency != nil {
+		in, out := &in.HourlyFrequency, &out.HourlyFrequency
+		*out = new(int32)
+		**out = **in
+	}
+	if in.DaysOfWeek != nil {
+		in, out := &in.DaysOfWeek, &out.DaysOfWeek
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.DaysOfMonth != nil {
+		in, out := &in.DaysOfMonth, &out.DaysOfMonth
+		*out = make([]int32, len(*in))
+		copy(*out, *in)
+	}
+	if in.WeekDayOfMonth != nil {
+		in, out := &in.WeekDayOfMonth, &out.WeekDayOfMonth
+		*out = new(WeekDayOfMonth)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Months != nil {
+		in, out := &in.Months, &out.Months
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.BackupWindow != nil {
+		in, out := &in.BackupWindow, &out.BackupWindow
+		*out = new(BackupWindow)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.TimeZone != nil {
+		in, out := &in.TimeZone, &out.TimeZone
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StandardSchedule.
+func (in *StandardSchedule) DeepCopy() *StandardSchedule {
+	if in == nil {
+		return nil
+	}
+	out := new(StandardSchedule)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WeekDayOfMonth) DeepCopyInto(out *WeekDayOfMonth) {
+	*out = *in
+	if in.WeekOfMonth != nil {
+		in, out := &in.WeekOfMonth, &out.WeekOfMonth
+		*out = new(string)
+		**out = **in
+	}
+	if in.DayOfWeek != nil {
+		in, out := &in.DayOfWeek, &out.DayOfWeek
+		*out = new(string)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WeekDayOfMonth.
+func (in *WeekDayOfMonth) DeepCopy() *WeekDayOfMonth {
+	if in == nil {
+		return nil
+	}
+	out := new(WeekDayOfMonth)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *WorkforceIdentityBasedManagementURI) DeepCopyInto(out *WorkforceIdentityBasedManagementURI) {
 	*out = *in
@@ -398,21 +752,6 @@ func (in *WorkforceIdentityBasedManagementURIObservedState) DeepCopy() *Workforc
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *WorkforceIdentityBasedOAuth2ClientID) DeepCopyInto(out *WorkforceIdentityBasedOAuth2ClientID) {
-	*out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkforceIdentityBasedOAuth2ClientID.
-func (in *WorkforceIdentityBasedOAuth2ClientID) DeepCopy() *WorkforceIdentityBasedOAuth2ClientID {
-	if in == nil {
-		return nil
-	}
-	out := new(WorkforceIdentityBasedOAuth2ClientID)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *WorkforceIdentityBasedOAuth2ClientIDObservedState) DeepCopyInto(out *WorkforceIdentityBasedOAuth2ClientIDObservedState) {
 	*out = *in
diff --git a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_backupdrbackupplans.backupdr.cnrm.cloud.google.com.yaml b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_backupdrbackupplans.backupdr.cnrm.cloud.google.com.yaml
new file mode 100644
index 00000000000..810a9cfa5e0
--- /dev/null
+++ b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_backupdrbackupplans.backupdr.cnrm.cloud.google.com.yaml
@@ -0,0 +1,340 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cnrm.cloud.google.com/version: 0.0.0-dev
+  creationTimestamp: null
+  labels:
+    cnrm.cloud.google.com/managed-by-kcc: "true"
+    cnrm.cloud.google.com/system: "true"
+  name: backupdrbackupplans.backupdr.cnrm.cloud.google.com
+spec:
+  group: backupdr.cnrm.cloud.google.com
+  names:
+    categories:
+    - gcp
+    kind: BackupDRBackupPlan
+    listKind: BackupDRBackupPlanList
+    plural: backupdrbackupplans
+    shortNames:
+    - gcpbackupdrbackupplan
+    - gcpbackupdrbackupplans
+    singular: backupdrbackupplan
+  preserveUnknownFields: false
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .metadata.creationTimestamp
+      name: Age
+      type: date
+    - description: When 'True', the most recent reconcile of the resource succeeded
+      jsonPath: .status.conditions[?(@.type=='Ready')].status
+      name: Ready
+      type: string
+    - description: The reason for the value in 'Ready'
+      jsonPath: .status.conditions[?(@.type=='Ready')].reason
+      name: Status
+      type: string
+    - description: The last transition time for the value in 'Status'
+      jsonPath: .status.conditions[?(@.type=='Ready')].lastTransitionTime
+      name: Status Age
+      type: date
+    name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: BackupDRBackupPlan is the Schema for the BackupDRBackupPlan 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: BackupDRBackupPlanSpec defines the desired state of BackupDRBackupPlan
+            properties:
+              backupRules:
+                description: Required. The backup rules for this `BackupPlan`. There
+                  must be at least one `BackupRule` message.
+                items:
+                  properties:
+                    backupRetentionDays:
+                      description: |-
+                        Required. Configures the duration for which backup data will be kept. It is
+                         defined in “days”. The value should be greater than or equal to minimum
+                         enforced retention of the backup vault.
+
+                         Minimum value is 1 and maximum value is 90 for hourly backups.
+                         Minimum value is 1 and maximum value is 90 for daily backups.
+                         Minimum value is 7 and maximum value is 186 for weekly backups.
+                         Minimum value is 30 and maximum value is 732 for monthly backups.
+                         Minimum value is 365 and maximum value is 36159 for yearly backups.
+                      format: int32
+                      type: integer
+                    ruleID:
+                      description: Required. Immutable. The unique id of this `BackupRule`.
+                        The `rule_id` is unique per `BackupPlan`.The `rule_id` must
+                        start with a lowercase letter followed by up to 62 lowercase
+                        letters, numbers, or hyphens. Pattern, /[a-z][a-z0-9-]{,62}/.
+                      type: string
+                    standardSchedule:
+                      description: Required. Defines a schedule that runs within the
+                        confines of a defined window of time.
+                      properties:
+                        backupWindow:
+                          description: |-
+                            Required. A BackupWindow defines the window of day during which backup jobs
+                             will run. Jobs are queued at the beginning of the window and will be marked
+                             as `NOT_RUN` if they do not start by the end of the window.
+
+                             Note: running jobs will not be cancelled at the end of the window.
+                          properties:
+                            endHourOfDay:
+                              description: |-
+                                Required. The hour of day (1-24) when the window end for e.g. if value of
+                                 end hour of day is 10 that mean backup window end time is 10:00.
+
+                                 End hour of day should be greater than start hour of day.
+                                 0 <= start_hour_of_day < end_hour_of_day <= 24
+
+                                 End hour of day is not include in backup window that mean if
+                                 end_hour_of_day= 10 jobs should start before 10:00.
+                              format: int32
+                              type: integer
+                            startHourOfDay:
+                              description: Required. The hour of day (0-23) when the
+                                window starts for e.g. if value of start hour of day
+                                is 6 that mean backup window start at 6:00.
+                              format: int32
+                              type: integer
+                          type: object
+                        daysOfMonth:
+                          description: |-
+                            Optional. Specifies days of months like 1, 5, or 14 on which jobs will run.
+
+                             Values for `days_of_month` are only applicable for `recurrence_type`,
+                             `MONTHLY` and `YEARLY`. A validation error will occur if other values are
+                             supplied.
+                          items:
+                            format: int32
+                            type: integer
+                          type: array
+                        daysOfWeek:
+                          description: |-
+                            Optional. Specifies days of week like, MONDAY or TUESDAY, on which jobs
+                             will run.
+
+                             This is required for `recurrence_type`, `WEEKLY` and is not applicable
+                             otherwise. A validation error will occur if a value is supplied and
+                             `recurrence_type` is not `WEEKLY`.
+                          items:
+                            type: string
+                          type: array
+                        hourlyFrequency:
+                          description: |-
+                            Optional. Specifies frequency for hourly backups. A hourly frequency of 2
+                             means jobs will run every 2 hours from start time till end time defined.
+
+                             This is required for `recurrence_type`, `HOURLY` and is not applicable
+                             otherwise. A validation error will occur if a value is supplied and
+                             `recurrence_type` is not `HOURLY`.
+
+                             Value of hourly frequency should be between 6 and 23.
+
+                             Reason for limit : We found that there is bandwidth limitation of 3GB/S for
+                             GMI while taking a backup and 5GB/S while doing a restore. Given the amount
+                             of parallel backups and restore we are targeting, this will potentially
+                             take the backup time to mins and hours (in worst case scenario).
+                          format: int32
+                          type: integer
+                        months:
+                          description: |-
+                            Optional. Specifies the months of year, like `FEBRUARY` and/or `MAY`, on
+                             which jobs will run.
+
+                             This field is only applicable when `recurrence_type` is `YEARLY`. A
+                             validation error will occur if other values are supplied.
+                          items:
+                            type: string
+                          type: array
+                        recurrenceType:
+                          description: Required. Specifies the `RecurrenceType` for
+                            the schedule.
+                          type: string
+                        timeZone:
+                          description: Required. The time zone to be used when interpreting
+                            the schedule. The value of this field must be a time zone
+                            name from the IANA tz database. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+                            for the list of valid timezone names. For e.g., Europe/Paris.
+                          type: string
+                        weekDayOfMonth:
+                          description: |-
+                            Optional. Specifies a week day of the month like, FIRST SUNDAY or LAST
+                             MONDAY, on which jobs will run. This will be specified by two fields in
+                             `WeekDayOfMonth`, one for the day, e.g. `MONDAY`, and one for the week,
+                             e.g. `LAST`.
+
+                             This field is only applicable for `recurrence_type`, `MONTHLY` and
+                             `YEARLY`. A validation error will occur if other values are supplied.
+                          properties:
+                            dayOfWeek:
+                              description: Required. Specifies the day of the week.
+                              type: string
+                            weekOfMonth:
+                              description: Required. Specifies the week of the month.
+                              type: string
+                          type: object
+                      type: object
+                  type: object
+                type: array
+              backupVault:
+                description: 'Required. Resource name of backup vault which will be
+                  used as storage location for backups. Format: projects/{project}/locations/{location}/backupVaults/{backupvault}'
+                type: string
+              description:
+                description: |-
+                  Optional. The description of the `BackupPlan` resource.
+
+                   The description allows for additional details about `BackupPlan` and its
+                   use cases to be provided. An example description is the following:  "This
+                   is a backup plan that performs a daily backup at 6pm and retains data for 3
+                   months". The description must be at most 2048 characters.
+                type: string
+              etag:
+                description: Optional. `etag` is returned from the service in the
+                  response. As a user of the service, you may provide an etag value
+                  in this field to prevent stale resources.
+                type: string
+              labels:
+                additionalProperties:
+                  type: string
+                description: 'Optional. This collection of key/value pairs allows
+                  for custom labels to be supplied by the user.  Example, {"tag":
+                  "Weekly"}.'
+                type: object
+              location:
+                description: Immutable.
+                type: string
+                x-kubernetes-validations:
+                - message: Location field is immutable
+                  rule: self == oldSelf
+              projectRef:
+                description: The Project that this resource belongs to.
+                oneOf:
+                - not:
+                    required:
+                    - external
+                  required:
+                  - name
+                - not:
+                    anyOf:
+                    - required:
+                      - name
+                    - required:
+                      - namespace
+                  required:
+                  - external
+                properties:
+                  external:
+                    description: The `projectID` field of a project, when not managed
+                      by Config Connector.
+                    type: string
+                  kind:
+                    description: The kind of the Project resource; optional but must
+                      be `Project` if provided.
+                    type: string
+                  name:
+                    description: The `name` field of a `Project` resource.
+                    type: string
+                  namespace:
+                    description: The `namespace` field of a `Project` resource.
+                    type: string
+                type: object
+              resourceID:
+                description: The BackupDRBackupPlan name. If not given, the metadata.name
+                  will be used.
+                type: string
+              resourceType:
+                description: Required. The resource type to which the `BackupPlan`
+                  will be applied. Examples include, "compute.googleapis.com/Instance",
+                  "sqladmin.googleapis.com/Instance", or "alloydb.googleapis.com/Cluster".
+                type: string
+            required:
+            - location
+            - projectRef
+            type: object
+          status:
+            description: BackupDRBackupPlanStatus defines the config connector machine
+              state of BackupDRBackupPlan
+            properties:
+              conditions:
+                description: Conditions represent the latest available observations
+                  of the object's current state.
+                items:
+                  properties:
+                    lastTransitionTime:
+                      description: Last time the condition transitioned from one status
+                        to another.
+                      type: string
+                    message:
+                      description: Human-readable message indicating details about
+                        last transition.
+                      type: string
+                    reason:
+                      description: Unique, one-word, CamelCase reason for the condition's
+                        last transition.
+                      type: string
+                    status:
+                      description: Status is the status of the condition. Can be True,
+                        False, Unknown.
+                      type: string
+                    type:
+                      description: Type is the type of the condition.
+                      type: string
+                  type: object
+                type: array
+              externalRef:
+                description: A unique specifier for the BackupDRBackupPlan resource
+                  in GCP.
+                type: string
+              observedGeneration:
+                description: ObservedGeneration is the generation of the resource
+                  that was most recently observed by the Config Connector controller.
+                  If this is equal to metadata.generation, then that means that the
+                  current reported status reflects the most recent desired state of
+                  the resource.
+                format: int64
+                type: integer
+              observedState:
+                description: ObservedState is the state of the resource as most recently
+                  observed in GCP.
+                properties:
+                  backupVaultServiceAccount:
+                    description: Output only. The Google Cloud Platform Service Account
+                      to be used by the BackupVault for taking backups. Specify the
+                      email address of the Backup Vault Service Account.
+                    type: string
+                  createTime:
+                    description: Output only. When the `BackupPlan` was created.
+                    type: string
+                  state:
+                    description: Output only. The `State` for the `BackupPlan`.
+                    type: string
+                  updateTime:
+                    description: Output only. When the `BackupPlan` was last updated.
+                    type: string
+                type: object
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/pkg/controller/direct/backupdr/backupplan_fuzzer.go b/pkg/controller/direct/backupdr/backupplan_fuzzer.go
new file mode 100644
index 00000000000..3cf8c2fa1af
--- /dev/null
+++ b/pkg/controller/direct/backupdr/backupplan_fuzzer.go
@@ -0,0 +1,51 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+// +tool:fuzz-gen
+// proto.message: google.cloud.backupdr.v1.BackupPlan
+// api.group: backupdr.cnrm.cloud.google.com
+
+package backupdr
+
+import (
+	pb "cloud.google.com/go/backupdr/apiv1/backupdrpb"
+	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/fuzztesting"
+)
+
+func init() {
+	fuzztesting.RegisterKRMFuzzer(backupDRBackupPlanFuzzer())
+}
+
+func backupDRBackupPlanFuzzer() fuzztesting.KRMFuzzer {
+	f := fuzztesting.NewKRMTypedFuzzer(&pb.BackupPlan{},
+		BackupDRBackupPlanSpec_FromProto, BackupDRBackupPlanSpec_ToProto,
+		BackupDRBackupPlanObservedState_FromProto, BackupDRBackupPlanObservedState_ToProto,
+	)
+
+	f.SpecFields.Insert(".description")
+	f.SpecFields.Insert(".labels")
+	f.SpecFields.Insert(".backup_rules")
+	f.SpecFields.Insert(".resource_type")
+	f.SpecFields.Insert(".etag")
+	f.SpecFields.Insert(".backup_vault")
+
+	f.StatusFields.Insert(".create_time")
+	f.StatusFields.Insert(".update_time")
+	f.StatusFields.Insert(".state")
+	f.StatusFields.Insert(".backup_vault_service_account")
+
+	f.UnimplementedFields.Insert(".name") // special field
+
+	return f
+}
diff --git a/pkg/controller/direct/backupdr/backupplan_mappers.go b/pkg/controller/direct/backupdr/backupplan_mappers.go
new file mode 100644
index 00000000000..7741dd7171c
--- /dev/null
+++ b/pkg/controller/direct/backupdr/backupplan_mappers.go
@@ -0,0 +1,53 @@
+// Copyright 2025 Google LLC
+//
+// 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.
+
+// +tool:fuzz-gen
+// proto.message: google.cloud.backupdr.v1.ManagementServer
+// api.group: backupdr.cnrm.cloud.google.com
+
+package backupdr
+
+import (
+	pb "cloud.google.com/go/backupdr/apiv1/backupdrpb"
+	krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/backupdr/v1alpha1"
+	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct"
+
+	dayofweekpb "google.golang.org/genproto/googleapis/type/dayofweek"
+	monthpb "google.golang.org/genproto/googleapis/type/month"
+)
+
+func StandardSchedule_ToProto(mapCtx *direct.MapContext, in *krm.StandardSchedule) *pb.StandardSchedule {
+	if in == nil {
+		return nil
+	}
+	out := &pb.StandardSchedule{}
+	out.RecurrenceType = direct.Enum_ToProto[pb.StandardSchedule_RecurrenceType](mapCtx, in.RecurrenceType)
+	out.HourlyFrequency = direct.ValueOf(in.HourlyFrequency)
+	out.DaysOfWeek = direct.EnumSlice_ToProto[dayofweekpb.DayOfWeek](mapCtx, in.DaysOfWeek)
+	out.DaysOfMonth = in.DaysOfMonth
+	out.WeekDayOfMonth = WeekDayOfMonth_ToProto(mapCtx, in.WeekDayOfMonth)
+	out.Months = direct.EnumSlice_ToProto[monthpb.Month](mapCtx, in.Months)
+	out.BackupWindow = BackupWindow_ToProto(mapCtx, in.BackupWindow)
+	out.TimeZone = direct.ValueOf(in.TimeZone)
+	return out
+}
+func WeekDayOfMonth_ToProto(mapCtx *direct.MapContext, in *krm.WeekDayOfMonth) *pb.WeekDayOfMonth {
+	if in == nil {
+		return nil
+	}
+	out := &pb.WeekDayOfMonth{}
+	out.WeekOfMonth = direct.Enum_ToProto[pb.WeekDayOfMonth_WeekOfMonth](mapCtx, in.WeekOfMonth)
+	out.DayOfWeek = direct.Enum_ToProto[dayofweekpb.DayOfWeek](mapCtx, in.DayOfWeek)
+	return out
+}
diff --git a/pkg/controller/direct/backupdr/managementserver_mappers.go b/pkg/controller/direct/backupdr/managementserver_mappers.go
index 7703ef56746..bf82722dbaf 100644
--- a/pkg/controller/direct/backupdr/managementserver_mappers.go
+++ b/pkg/controller/direct/backupdr/managementserver_mappers.go
@@ -21,6 +21,7 @@ package backupdr
 import (
 	pb "cloud.google.com/go/backupdr/apiv1/backupdrpb"
 	krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/backupdr/v1alpha1"
+	refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
 	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct"
 )
 
@@ -92,3 +93,14 @@ func BackupDRManagementServerObservedState_ToProto(mapCtx *direct.MapContext, in
 	out.BaProxyUri = in.BAProxyURIs
 	return out
 }
+func NetworkConfig_FromProto(mapCtx *direct.MapContext, in *pb.NetworkConfig) *krm.NetworkConfig {
+	if in == nil {
+		return nil
+	}
+	out := &krm.NetworkConfig{}
+	if in.GetNetwork() != "" {
+		out.NetworkRef = &refsv1beta1.ComputeNetworkRef{External: in.GetNetwork()}
+	}
+	out.PeeringMode = direct.Enum_FromProto(mapCtx, in.GetPeeringMode())
+	return out
+}
diff --git a/pkg/controller/direct/backupdr/mapper.generated.go b/pkg/controller/direct/backupdr/mapper.generated.go
index afa7546149d..841e53a7f5a 100644
--- a/pkg/controller/direct/backupdr/mapper.generated.go
+++ b/pkg/controller/direct/backupdr/mapper.generated.go
@@ -16,12 +16,62 @@ package backupdr
 
 import (
 	pb "cloud.google.com/go/backupdr/apiv1/backupdrpb"
-
 	krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/backupdr/v1alpha1"
-	refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
 	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct"
 )
 
+func BackupDRBackupPlanObservedState_FromProto(mapCtx *direct.MapContext, in *pb.BackupPlan) *krm.BackupDRBackupPlanObservedState {
+	if in == nil {
+		return nil
+	}
+	out := &krm.BackupDRBackupPlanObservedState{}
+	// MISSING: Name
+	out.CreateTime = direct.StringTimestamp_FromProto(mapCtx, in.GetCreateTime())
+	out.UpdateTime = direct.StringTimestamp_FromProto(mapCtx, in.GetUpdateTime())
+	out.State = direct.Enum_FromProto(mapCtx, in.GetState())
+	out.BackupVaultServiceAccount = direct.LazyPtr(in.GetBackupVaultServiceAccount())
+	return out
+}
+func BackupDRBackupPlanObservedState_ToProto(mapCtx *direct.MapContext, in *krm.BackupDRBackupPlanObservedState) *pb.BackupPlan {
+	if in == nil {
+		return nil
+	}
+	out := &pb.BackupPlan{}
+	// MISSING: Name
+	out.CreateTime = direct.StringTimestamp_ToProto(mapCtx, in.CreateTime)
+	out.UpdateTime = direct.StringTimestamp_ToProto(mapCtx, in.UpdateTime)
+	out.State = direct.Enum_ToProto[pb.BackupPlan_State](mapCtx, in.State)
+	out.BackupVaultServiceAccount = direct.ValueOf(in.BackupVaultServiceAccount)
+	return out
+}
+func BackupDRBackupPlanSpec_FromProto(mapCtx *direct.MapContext, in *pb.BackupPlan) *krm.BackupDRBackupPlanSpec {
+	if in == nil {
+		return nil
+	}
+	out := &krm.BackupDRBackupPlanSpec{}
+	// MISSING: Name
+	out.Description = direct.LazyPtr(in.GetDescription())
+	out.Labels = in.Labels
+	out.BackupRules = direct.Slice_FromProto(mapCtx, in.BackupRules, BackupRule_FromProto)
+	out.ResourceType = direct.LazyPtr(in.GetResourceType())
+	out.Etag = direct.LazyPtr(in.GetEtag())
+	out.BackupVault = direct.LazyPtr(in.GetBackupVault())
+	return out
+}
+func BackupDRBackupPlanSpec_ToProto(mapCtx *direct.MapContext, in *krm.BackupDRBackupPlanSpec) *pb.BackupPlan {
+	if in == nil {
+		return nil
+	}
+	out := &pb.BackupPlan{}
+	// MISSING: Name
+	out.Description = direct.ValueOf(in.Description)
+	out.Labels = in.Labels
+	out.BackupRules = direct.Slice_ToProto(mapCtx, in.BackupRules, BackupRule_ToProto)
+	out.ResourceType = direct.ValueOf(in.ResourceType)
+	out.Etag = direct.ValueOf(in.Etag)
+	out.BackupVault = direct.ValueOf(in.BackupVault)
+	return out
+}
 func BackupDRManagementServerSpec_FromProto(mapCtx *direct.MapContext, in *pb.ManagementServer) *krm.BackupDRManagementServerSpec {
 	if in == nil {
 		return nil
@@ -58,33 +108,44 @@ func BackupDRManagementServerSpec_ToProto(mapCtx *direct.MapContext, in *krm.Bac
 	// MISSING: SatisfiesPzi
 	return out
 }
-func ManagementURI_FromProto(mapCtx *direct.MapContext, in *pb.ManagementURI) *krm.ManagementURI {
+func BackupRule_FromProto(mapCtx *direct.MapContext, in *pb.BackupRule) *krm.BackupRule {
 	if in == nil {
 		return nil
 	}
-	out := &krm.ManagementURI{}
-	// MISSING: WebUi
-	// MISSING: API
+	out := &krm.BackupRule{}
+	out.RuleID = direct.LazyPtr(in.GetRuleId())
+	out.BackupRetentionDays = direct.LazyPtr(in.GetBackupRetentionDays())
+	out.StandardSchedule = StandardSchedule_FromProto(mapCtx, in.GetStandardSchedule())
 	return out
 }
-func ManagementURI_ToProto(mapCtx *direct.MapContext, in *krm.ManagementURI) *pb.ManagementURI {
+func BackupRule_ToProto(mapCtx *direct.MapContext, in *krm.BackupRule) *pb.BackupRule {
 	if in == nil {
 		return nil
 	}
-	out := &pb.ManagementURI{}
-	// MISSING: WebUi
-	// MISSING: API
+	out := &pb.BackupRule{}
+	out.RuleId = direct.ValueOf(in.RuleID)
+	out.BackupRetentionDays = direct.ValueOf(in.BackupRetentionDays)
+	if oneof := StandardSchedule_ToProto(mapCtx, in.StandardSchedule); oneof != nil {
+		out.BackupScheduleOneof = &pb.BackupRule_StandardSchedule{StandardSchedule: oneof}
+	}
 	return out
 }
-func NetworkConfig_FromProto(mapCtx *direct.MapContext, in *pb.NetworkConfig) *krm.NetworkConfig {
+func BackupWindow_FromProto(mapCtx *direct.MapContext, in *pb.BackupWindow) *krm.BackupWindow {
 	if in == nil {
 		return nil
 	}
-	out := &krm.NetworkConfig{}
-	if in.GetNetwork() != "" {
-		out.NetworkRef = &refsv1beta1.ComputeNetworkRef{External: in.GetNetwork()}
+	out := &krm.BackupWindow{}
+	out.StartHourOfDay = direct.LazyPtr(in.GetStartHourOfDay())
+	out.EndHourOfDay = direct.LazyPtr(in.GetEndHourOfDay())
+	return out
+}
+func BackupWindow_ToProto(mapCtx *direct.MapContext, in *krm.BackupWindow) *pb.BackupWindow {
+	if in == nil {
+		return nil
 	}
-	out.PeeringMode = direct.Enum_FromProto(mapCtx, in.GetPeeringMode())
+	out := &pb.BackupWindow{}
+	out.StartHourOfDay = direct.ValueOf(in.StartHourOfDay)
+	out.EndHourOfDay = direct.ValueOf(in.EndHourOfDay)
 	return out
 }
 func NetworkConfig_ToProto(mapCtx *direct.MapContext, in *krm.NetworkConfig) *pb.NetworkConfig {
@@ -98,6 +159,30 @@ func NetworkConfig_ToProto(mapCtx *direct.MapContext, in *krm.NetworkConfig) *pb
 	out.PeeringMode = direct.Enum_ToProto[pb.NetworkConfig_PeeringMode](mapCtx, in.PeeringMode)
 	return out
 }
+func StandardSchedule_FromProto(mapCtx *direct.MapContext, in *pb.StandardSchedule) *krm.StandardSchedule {
+	if in == nil {
+		return nil
+	}
+	out := &krm.StandardSchedule{}
+	out.RecurrenceType = direct.Enum_FromProto(mapCtx, in.GetRecurrenceType())
+	out.HourlyFrequency = direct.LazyPtr(in.GetHourlyFrequency())
+	out.DaysOfWeek = direct.EnumSlice_FromProto(mapCtx, in.DaysOfWeek)
+	out.DaysOfMonth = in.DaysOfMonth
+	out.WeekDayOfMonth = WeekDayOfMonth_FromProto(mapCtx, in.GetWeekDayOfMonth())
+	out.Months = direct.EnumSlice_FromProto(mapCtx, in.Months)
+	out.BackupWindow = BackupWindow_FromProto(mapCtx, in.GetBackupWindow())
+	out.TimeZone = direct.LazyPtr(in.GetTimeZone())
+	return out
+}
+func WeekDayOfMonth_FromProto(mapCtx *direct.MapContext, in *pb.WeekDayOfMonth) *krm.WeekDayOfMonth {
+	if in == nil {
+		return nil
+	}
+	out := &krm.WeekDayOfMonth{}
+	out.WeekOfMonth = direct.Enum_FromProto(mapCtx, in.GetWeekOfMonth())
+	out.DayOfWeek = direct.Enum_FromProto(mapCtx, in.GetDayOfWeek())
+	return out
+}
 func WorkforceIdentityBasedManagementURI_FromProto(mapCtx *direct.MapContext, in *pb.WorkforceIdentityBasedManagementURI) *krm.WorkforceIdentityBasedManagementURI {
 	if in == nil {
 		return nil
@@ -134,21 +219,3 @@ func WorkforceIdentityBasedManagementURIObservedState_ToProto(mapCtx *direct.Map
 	out.ThirdPartyManagementUri = direct.ValueOf(in.ThirdPartyManagementURI)
 	return out
 }
-func WorkforceIdentityBasedOAuth2ClientID_FromProto(mapCtx *direct.MapContext, in *pb.WorkforceIdentityBasedOAuth2ClientID) *krm.WorkforceIdentityBasedOAuth2ClientID {
-	if in == nil {
-		return nil
-	}
-	out := &krm.WorkforceIdentityBasedOAuth2ClientID{}
-	// MISSING: FirstPartyOAUTH2ClientID
-	// MISSING: ThirdPartyOAUTH2ClientID
-	return out
-}
-func WorkforceIdentityBasedOAuth2ClientID_ToProto(mapCtx *direct.MapContext, in *krm.WorkforceIdentityBasedOAuth2ClientID) *pb.WorkforceIdentityBasedOAuth2ClientID {
-	if in == nil {
-		return nil
-	}
-	out := &pb.WorkforceIdentityBasedOAuth2ClientID{}
-	// MISSING: FirstPartyOAUTH2ClientID
-	// MISSING: ThirdPartyOAUTH2ClientID
-	return out
-}
diff --git a/pkg/gvks/supportedgvks/gvks_generated.go b/pkg/gvks/supportedgvks/gvks_generated.go
index b7410bf01c3..ef59d55e6ad 100644
--- a/pkg/gvks/supportedgvks/gvks_generated.go
+++ b/pkg/gvks/supportedgvks/gvks_generated.go
@@ -467,6 +467,16 @@ var SupportedGVKs = map[schema.GroupVersionKind]GVKMetadata{
 			"cnrm.cloud.google.com/tf2crd":          "true",
 		},
 	},
+	{
+		Group:   "backupdr.cnrm.cloud.google.com",
+		Version: "v1alpha1",
+		Kind:    "BackupDRBackupPlan",
+	}: {
+		Labels: map[string]string{
+			"cnrm.cloud.google.com/managed-by-kcc": "true",
+			"cnrm.cloud.google.com/system":         "true",
+		},
+	},
 	{
 		Group:   "backupdr.cnrm.cloud.google.com",
 		Version: "v1alpha1",
diff --git a/pkg/test/fuzz/generate.go b/pkg/test/fuzz/generate.go
index 0e778ed5ae5..a5ffe0b50b1 100644
--- a/pkg/test/fuzz/generate.go
+++ b/pkg/test/fuzz/generate.go
@@ -94,6 +94,9 @@ func fillWithRandom0(t *testing.T, randStream *rand.Rand, msg protoreflect.Messa
 					listVal.Append(protoreflect.ValueOf(val.Number()))
 				}
 
+			case protoreflect.Int32Kind:
+				// TODO: handle []int32
+
 			default:
 				t.Fatalf("unhandled field kind %v: %v", field.Kind(), field)
 			}
diff --git a/tests/apichecks/testdata/exceptions/missingrefs.txt b/tests/apichecks/testdata/exceptions/missingrefs.txt
index 1f3d9a9d162..a90f14b117c 100644
--- a/tests/apichecks/testdata/exceptions/missingrefs.txt
+++ b/tests/apichecks/testdata/exceptions/missingrefs.txt
@@ -6,6 +6,7 @@
 [refs] crd=alloydbbackups.alloydb.cnrm.cloud.google.com version=v1beta1: field ".spec.encryptionConfig.kmsKeyName" should be a reference
 [refs] crd=apigatewaygateways.apigateway.cnrm.cloud.google.com version=v1alpha1: field ".spec.apiConfig" should be a reference
 [refs] crd=apigeenataddresses.apigee.cnrm.cloud.google.com version=v1alpha1: field ".spec.instanceId" should be a reference
+[refs] crd=backupdrbackupplans.backupdr.cnrm.cloud.google.com version=v1alpha1: field ".spec.backupVault" should be a reference
 [refs] crd=beyondcorpappconnections.beyondcorp.cnrm.cloud.google.com version=v1alpha1: field ".spec.gateway.appGateway" should be a reference
 [refs] crd=bigquerydatapolicydatapolicies.bigquerydatapolicy.cnrm.cloud.google.com version=v1alpha1: field ".spec.policyTag" should be a reference
 [refs] crd=bigquerytables.bigquery.cnrm.cloud.google.com version=v1beta1: field ".spec.externalDataConfiguration.connectionId" should be a reference


Reviews:
user: login: yuwenma
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T16:46:22Z
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T20:47:25Z
user: login: yuwenma
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T20:52:01Z
user: login: jingyih
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T23:40:35Z

Review comments:
diff_hunk: @@ -6,6 +6,7 @@
 [refs] crd=alloydbbackups.alloydb.cnrm.cloud.google.com version=v1beta1: field ".spec.encryptionConfig.kmsKeyName" should be a reference
 [refs] crd=apigatewaygateways.apigateway.cnrm.cloud.google.com version=v1alpha1: field ".spec.apiConfig" should be a reference
 [refs] crd=apigeenataddresses.apigee.cnrm.cloud.google.com version=v1alpha1: field ".spec.instanceId" should be a reference
+[refs] crd=backupdrbackupplans.backupdr.cnrm.cloud.google.com version=v1alpha1: field ".spec.backupVault" should be a reference
path: tests/apichecks/testdata/exceptions/missingrefs.txt
user: login: yuwenma
body: Please fix this field to use a reference.
created_at: 2025-03-13T16:46:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,6 +6,7 @@
 [refs] crd=alloydbbackups.alloydb.cnrm.cloud.google.com version=v1beta1: field ".spec.encryptionConfig.kmsKeyName" should be a reference
 [refs] crd=apigatewaygateways.apigateway.cnrm.cloud.google.com version=v1alpha1: field ".spec.apiConfig" should be a reference
 [refs] crd=apigeenataddresses.apigee.cnrm.cloud.google.com version=v1alpha1: field ".spec.instanceId" should be a reference
+[refs] crd=backupdrbackupplans.backupdr.cnrm.cloud.google.com version=v1alpha1: field ".spec.backupVault" should be a reference
path: tests/apichecks/testdata/exceptions/missingrefs.txt
user: login: jingyih
body: I'm working on backupVault, but our current tooling conductor doesn't handle multiple resources under the same API group very well. I'd prefer to merge this first, land backupVault, and then address the reference issue (I've added a TODO in the code).
created_at: 2025-03-13T20:47:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,6 +6,7 @@
 [refs] crd=alloydbbackups.alloydb.cnrm.cloud.google.com version=v1beta1: field ".spec.encryptionConfig.kmsKeyName" should be a reference
 [refs] crd=apigatewaygateways.apigateway.cnrm.cloud.google.com version=v1alpha1: field ".spec.apiConfig" should be a reference
 [refs] crd=apigeenataddresses.apigee.cnrm.cloud.google.com version=v1alpha1: field ".spec.instanceId" should be a reference
+[refs] crd=backupdrbackupplans.backupdr.cnrm.cloud.google.com version=v1alpha1: field ".spec.backupVault" should be a reference
path: tests/apichecks/testdata/exceptions/missingrefs.txt
user: login: yuwenma
body: kk. works for me. Another option is that you can run generate-types to create the backupvault_reference.go file and comment out other code you don't want to checkin. 
created_at: 2025-03-13T20:52:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,6 +6,7 @@
 [refs] crd=alloydbbackups.alloydb.cnrm.cloud.google.com version=v1beta1: field ".spec.encryptionConfig.kmsKeyName" should be a reference
 [refs] crd=apigatewaygateways.apigateway.cnrm.cloud.google.com version=v1alpha1: field ".spec.apiConfig" should be a reference
 [refs] crd=apigeenataddresses.apigee.cnrm.cloud.google.com version=v1alpha1: field ".spec.instanceId" should be a reference
+[refs] crd=backupdrbackupplans.backupdr.cnrm.cloud.google.com version=v1alpha1: field ".spec.backupVault" should be a reference
path: tests/apichecks/testdata/exceptions/missingrefs.txt
user: login: jingyih
body: Addressing in https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/4017
created_at: 2025-03-13T23:40:34Z
author_association: CONTRIBUTOR

PR comments:
user: login: yuwenma
created_at: 2025-03-13T20:52:07Z
body: /lgtm
/approve
author_association: CONTRIBUTOR
user: login: google-oss-prow[bot]
created_at: 2025-03-13T20:52:19Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/GoogleCloudPlatform/k8s-config-connector/pull/3985#issuecomment-2722665097" title="Approved">yuwenma</a>*

The full list of commands accepted by this bot can be found [here](https://oss.gprow.dev/command-help?repo=GoogleCloudPlatform%2Fk8s-config-connector).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/GoogleCloudPlatform/k8s-config-connector/blob/master/OWNERS)~~ [yuwenma]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR