Repository: radius-project/radius
Pull request #9165: Fix radius-controller waiting on `sourcev1.GitRepository` CRD
Author: login: willdavsmith
Created: 2025-04-11T19:18:02Z | Merged: 2025-04-17T17:18:58Z

# Description

* Fixing issue where radius-controller is expecting GitRepository CRD to be installed

## Type of change

<!--

Please select **one** of the following options that describes your change and delete the others. Clearly identifying the type of change you are making will help us review your PR faster, and is used in authoring release notes.

If you are making a bug fix or functionality change to Radius and do not have an associated issue link please create one now. 

-->

- This pull request fixes a bug in Radius and has an approved issue (issue link required).

<!--

Please update the following to link the associated issue. This is required for some kinds of changes (see above).

-->

Fixes: https://github.com/radius-project/radius/issues/9148

## Contributor checklist
Please verify that the PR meets the following requirements, where applicable:

- An overview of proposed schema changes is included in a linked GitHub issue.
    - [ ] Yes
    - [ ] Not applicable
- A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced.
    - [ ] Yes
    - [ ] Not applicable
- The design document has been reviewed and approved by Radius maintainers/approvers.
    - [ ] Yes
    - [ ] Not applicable
- A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR.
    - [ ] Yes
    - [ ] Not applicable
- A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made.
    - [ ] Yes
    - [ ] Not applicable
- A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR.
    - [ ] Yes
    - [ ] Not applicable


Diff:
diff --git a/deploy/Chart/templates/controller/rbac.yaml b/deploy/Chart/templates/controller/rbac.yaml
index 0a6870ad13..27f929beb4 100644
--- a/deploy/Chart/templates/controller/rbac.yaml
+++ b/deploy/Chart/templates/controller/rbac.yaml
@@ -70,6 +70,14 @@ rules:
   - gitrepositories/status
   verbs:
   - get
+- apiGroups:
+  - "*"
+  resources:
+  - customresourcedefinitions
+  verbs:
+  - watch
+  - list
+  - get
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
diff --git a/pkg/cli/kubernetes/kubernetes.go b/pkg/cli/kubernetes/kubernetes.go
index bbd236ec44..17c68892b3 100644
--- a/pkg/cli/kubernetes/kubernetes.go
+++ b/pkg/cli/kubernetes/kubernetes.go
@@ -42,6 +42,7 @@ import (
 	"github.com/radius-project/radius/pkg/cli/output"
 	radappiov1alpha3 "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3"
 	"github.com/radius-project/radius/pkg/kubeutil"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 
 	// Import kubernetes auth plugins
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -61,6 +62,7 @@ func init() {
 	_ = contourv1.AddToScheme(Scheme)
 	_ = radappiov1alpha3.AddToScheme(Scheme)
 	_ = sourcev1.AddToScheme(Scheme)
+	_ = apiextensionsv1.AddToScheme(Scheme)
 }
 
 // NewDynamicClient creates a new dynamic client by context name, otherwise returns an error.
diff --git a/pkg/controller/reconciler/flux_controller.go b/pkg/controller/reconciler/flux_controller.go
index ed55cebbf7..f77d6fd058 100644
--- a/pkg/controller/reconciler/flux_controller.go
+++ b/pkg/controller/reconciler/flux_controller.go
@@ -9,6 +9,7 @@ import (
 	"path"
 	"path/filepath"
 	"strings"
+	"sync/atomic"
 
 	"github.com/go-logr/logr"
 	"github.com/radius-project/radius/pkg/cli/bicep"
@@ -17,11 +18,14 @@ import (
 	"github.com/radius-project/radius/pkg/ucp/ucplog"
 	"gopkg.in/yaml.v3"
 	corev1 "k8s.io/api/core/v1"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
 
 	sourcev1 "github.com/fluxcd/source-controller/api/v1"
 
@@ -44,6 +48,7 @@ type FluxController struct {
 	Bicep          bicep.Interface
 	FileSystem     filesystem.FileSystem
 	ArchiveFetcher ArchiveFetcher
+	initialized    *atomic.Bool // Track if we've initialized the controller
 }
 
 // RadiusGitOpsConfig is the configuration for Radius in a Git repository.
@@ -73,13 +78,77 @@ func deploymentTemplateRepositoryIndexer(o client.Object) []string {
 }
 
 func (r *FluxController) SetupWithManager(mgr ctrl.Manager) error {
+	r.initialized = &atomic.Bool{}
+
+	// Register a controller for CustomResourceDefinition events.
+	// We want to watch for the GitRepository CRD to be created or updated.
+	err := ctrl.NewControllerManagedBy(mgr).
+		For(&apiextensionsv1.CustomResourceDefinition{}).
+		WithEventFilter(predicate.Funcs{
+			CreateFunc: func(e event.CreateEvent) bool {
+				return r.registerFluxController(e.Object, mgr)
+			},
+			UpdateFunc: func(e event.UpdateEvent) bool {
+				return r.registerFluxController(e.ObjectNew, mgr)
+			},
+			DeleteFunc: func(e event.DeleteEvent) bool {
+				return false
+			},
+			GenericFunc: func(e event.GenericEvent) bool {
+				return false
+			},
+		}).
+		Complete(r)
+
+	return err
+}
+
+// registerFluxController registers the FluxController with the manager
+// when the GitRepository CRD is created or updated.
+func (r *FluxController) registerFluxController(obj client.Object, mgr ctrl.Manager) bool {
+	// Only process if:
+	// 1. It's the GitRepository CRD
+	// 2. The controller isn't already initialized
+	// 3. The CRD is established (fully registered with the API server)
+	if !isGitRepositoryCRD(obj) || r.initialized.Load() {
+		return false
+	}
+
+	// Check if the CRD is established before setting up the controller
+	crd := obj.(*apiextensionsv1.CustomResourceDefinition)
+	for _, condition := range crd.Status.Conditions {
+		if condition.Type == apiextensionsv1.Established &&
+			condition.Status == apiextensionsv1.ConditionTrue {
+			// CRD is established, set up the GitRepository controller
+			err := r.setupFluxController(mgr)
+			if err != nil {
+				ucplog.FromContextOrDiscard(context.Background()).Error(
+					err, "failed to setup GitRepository controller")
+			}
+			return false // We don't need to reconcile CRDs
+		}
+	}
+
+	return false // Not established yet
+}
+
+// setupFluxController creates a new controller for GitRepository objects
+// and sets it up with the manager.
+func (r *FluxController) setupFluxController(mgr ctrl.Manager) error {
 	if err := mgr.GetFieldIndexer().IndexField(context.Background(), &radappiov1alpha3.DeploymentTemplate{}, deploymentTemplateRepositoryField, deploymentTemplateRepositoryIndexer); err != nil {
 		return err
 	}
 
-	return ctrl.NewControllerManagedBy(mgr).
+	err := ctrl.NewControllerManagedBy(mgr).
 		For(&sourcev1.GitRepository{}, builder.WithPredicates(&GitRepositoryRevisionChangePredicate{})).
 		Complete(r)
+
+	if err != nil {
+		return err
+	}
+
+	r.initialized.Store(true)
+	return nil
 }
 
 // +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch
@@ -430,3 +499,26 @@ func isSpecifiedInConfig(fileName string, config []ConfigEntry) bool {
 
 	return false
 }
+
+// isGitRepositoryCRD checks if obj is a source.toolkit.fluxcd.io/v1/GitRepository CustomResourceDefinition
+func isGitRepositoryCRD(obj client.Object) bool {
+	crd, ok := obj.(*apiextensionsv1.CustomResourceDefinition)
+	if !ok {
+		return false
+	}
+
+	// Check if this is the GitRepository CRD
+	return crd.Spec.Group == "source.toolkit.fluxcd.io" &&
+		containsVersion(crd.Spec.Versions, "v1") &&
+		crd.Spec.Names.Kind == "GitRepository"
+}
+
+// containsVersion checks if the version list contains the target version
+func containsVersion(versions []apiextensionsv1.CustomResourceDefinitionVersion, targetVersion string) bool {
+	for _, v := range versions {
+		if v.Name == targetVersion {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/controller/reconciler/flux_controller_test.go b/pkg/controller/reconciler/flux_controller_test.go
index 57313f2642..7b4f29b4f2 100644
--- a/pkg/controller/reconciler/flux_controller_test.go
+++ b/pkg/controller/reconciler/flux_controller_test.go
@@ -30,7 +30,7 @@ import (
 	"github.com/radius-project/radius/pkg/to"
 	"github.com/radius-project/radius/test/testcontext"
 	"github.com/stretchr/testify/require"
-	"go.uber.org/mock/gomock"
+	gomock "go.uber.org/mock/gomock"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
diff --git a/pkg/controller/reconciler/main_test.go b/pkg/controller/reconciler/main_test.go
index aa00037307..db1b24f16e 100644
--- a/pkg/controller/reconciler/main_test.go
+++ b/pkg/controller/reconciler/main_test.go
@@ -24,6 +24,7 @@ import (
 	sourcev1 "github.com/fluxcd/source-controller/api/v1"
 	radappiov1alpha3 "github.com/radius-project/radius/pkg/controller/api/radapp.io/v1alpha3"
 	admissionv1 "k8s.io/api/admissionregistration/v1"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -74,6 +75,7 @@ func TestMain(m *testing.M) {
 	_ = clientgoscheme.AddToScheme(s)
 	_ = radappiov1alpha3.AddToScheme(s)
 	_ = sourcev1.AddToScheme(s)
+	_ = apiextensionsv1.AddToScheme(s)
 
 	config = cfg
 	scheme = s
diff --git a/pkg/controller/service.go b/pkg/controller/service.go
index 28e59992e9..8e2dc560bf 100644
--- a/pkg/controller/service.go
+++ b/pkg/controller/service.go
@@ -31,6 +31,7 @@ import (
 	"github.com/radius-project/radius/pkg/sdk"
 	sdkclients "github.com/radius-project/radius/pkg/sdk/clients"
 	"github.com/radius-project/radius/pkg/ucp/ucplog"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -48,6 +49,7 @@ func init() {
 	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
 	utilruntime.Must(radappiov1alpha3.AddToScheme(scheme))
 	utilruntime.Must(sourcev1.AddToScheme(scheme))
+	utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
 }
 
 var _ hosting.Service = (*Service)(nil)


Reviews:
user: login: willdavsmith
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T19:18:45Z
user: login: willdavsmith
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T19:20:00Z
user: login: sk593
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T23:33:44Z
user: login: sk593
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T23:33:51Z
user: login: willdavsmith
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T23:38:44Z

Review comments:
diff_hunk: @@ -70,6 +70,14 @@ rules:
   - gitrepositories/status
   verbs:
   - get
+- apiGroups:
+  - "*"
+  resources:
+  - customresourcedefinitions
path: deploy/Chart/templates/controller/rbac.yaml
user: login: willdavsmith
body: `CustomResourceDefinitions` is required now for Flux Controller to list `GitRepository` resources
created_at: 2025-04-11T19:18:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -73,13 +78,77 @@ func deploymentTemplateRepositoryIndexer(o client.Object) []string {
 }
 
 func (r *FluxController) SetupWithManager(mgr ctrl.Manager) error {
+	r.initialized = &atomic.Bool{}
+
+	// Register a controller for CustomResourceDefinition events.
+	// We want to watch for the GitRepository CRD to be created or updated.
+	err := ctrl.NewControllerManagedBy(mgr).
+		For(&apiextensionsv1.CustomResourceDefinition{}).
+		WithEventFilter(predicate.Funcs{
+			CreateFunc: func(e event.CreateEvent) bool {
+				return r.registerFluxController(e.Object, mgr)
+			},
+			UpdateFunc: func(e event.UpdateEvent) bool {
+				return r.registerFluxController(e.ObjectNew, mgr)
+			},
+			DeleteFunc: func(e event.DeleteEvent) bool {
+				return false
+			},
+			GenericFunc: func(e event.GenericEvent) bool {
+				return false
+			},
+		}).
+		Complete(r)
+
+	return err
+}
+
+// registerFluxController registers the FluxController with the manager
+// when the GitRepository CRD is created or updated.
+func (r *FluxController) registerFluxController(obj client.Object, mgr ctrl.Manager) bool {
path: pkg/controller/reconciler/flux_controller.go
user: login: willdavsmith
body: Need to do all of this roundabout registering in case `GitRepository` type doesn't exist on the cluster yet. This structure means that the controller will set up a manager for `CustomResourceDefinition`, wait for `GitRepository`, and finally register the FluxController once this is all set up.
created_at: 2025-04-11T19:19:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,6 +70,14 @@ rules:
   - gitrepositories/status
   verbs:
   - get
+- apiGroups:
+  - "*"
+  resources:
+  - customresourcedefinitions
path: deploy/Chart/templates/controller/rbac.yaml
user: login: sk593
body: Was this a change made by Flux? 
created_at: 2025-04-16T23:33:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,6 +70,14 @@ rules:
   - gitrepositories/status
   verbs:
   - get
+- apiGroups:
+  - "*"
+  resources:
+  - customresourcedefinitions
path: deploy/Chart/templates/controller/rbac.yaml
user: login: willdavsmith
body: no, I changed the controller to watch for GitRepositories so we don't crash on startup
created_at: 2025-04-16T23:38:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-11T19:29:41Z
body: ## Unit Tests
3 689 tests  ±0   3 687 ✅ ±0   6m 30s ⏱️ -17s
  296 suites ±0       2 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 7dda0521. ± Comparison against base commit 1fafe38d.

[test-results]:data:application/gzip;base64,H4sIAGFCAGgC/12MSw6DIBQAr2JYd/FE+fUyho8kpCoNwqrp3QvYKulyZpJ5IeuWeUf3rr91aE8uVsCCZjQpyOj8lsUgIIscY8kD5eKH0560Phy73MM9y+YUVroli3MyzSH48DUhbc210N/0UNezcrOs3B61X1cXMyBmjASCe2apsFzBSADMQAjTCviolAWQBFOF3h8qOx7sCwEAAA==

:recycle: This comment has been updated with latest results.
author_association: NONE
user: login: codecov[bot]
created_at: 2025-04-11T19:30:12Z
body: ## [Codecov](https://app.codecov.io/gh/radius-project/radius/pull/9165?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project) Report
Attention: Patch coverage is `65.62500%` with `22 lines` in your changes missing coverage. Please review.
> Project coverage is 55.73%. Comparing base [(`1fafe38`)](https://app.codecov.io/gh/radius-project/radius/commit/1fafe38dbb1c58a683034f7d82d1dc924b9e3a51?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project) to head [(`7dda052`)](https://app.codecov.io/gh/radius-project/radius/commit/7dda05217f69f8b04500d3557cb084bbf00a526b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/radius-project/radius/pull/9165?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project) | Patch % | Lines |
|---|---|---|
| [pkg/controller/reconciler/flux\_controller.go](https://app.codecov.io/gh/radius-project/radius/pull/9165?src=pr&el=tree&filepath=pkg%2Fcontroller%2Freconciler%2Fflux_controller.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project#diff-cGtnL2NvbnRyb2xsZXIvcmVjb25jaWxlci9mbHV4X2NvbnRyb2xsZXIuZ28=) | 66.12% | [18 Missing and 3 partials :warning: ](https://app.codecov.io/gh/radius-project/radius/pull/9165?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project) |
| [pkg/controller/service.go](https://app.codecov.io/gh/radius-project/radius/pull/9165?src=pr&el=tree&filepath=pkg%2Fcontroller%2Fservice.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project#diff-cGtnL2NvbnRyb2xsZXIvc2VydmljZS5nbw==) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/radius-project/radius/pull/9165?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #9165   +/-   ##
=======================================
  Coverage   55.72%   55.73%           
=======================================
  Files         602      602           
  Lines       41183    41246   +63     
=======================================
+ Hits        22950    22988   +38     
- Misses      16482    16503   +21     
- Partials     1751     1755    +4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/radius-project/radius/pull/9165?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=radius-project).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: radius-functional-tests[bot]
created_at: 2025-04-11T19:39:30Z
body: ## Radius functional test overview

:mag: **[Go to test action run](https://github.com/radius-project/radius/actions/runs/14410783757)**

| Name | Value |
|------|-------|
|**Repository** | radius-project/radius |
|**Commit ref** | 7277d93dad972a41ff1061c40ba49dc6a911f503 |
|**Unique ID** | func46b78d7472 |
|**Image tag** | pr-func46b78d7472 |

<details>
<summary> Click here to see the list of tools in the current test run</summary>

* gotestsum 1.12.0
* KinD: v0.20.0
* Dapr: 
* Azure KeyVault CSI driver: 1.4.2
* Azure Workload identity webhook: 1.3.0
* Bicep recipe location `ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-func46b78d7472`
* Terraform recipe location `http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip` (in cluster)
* applications-rp test image location: `ghcr.io/radius-project/dev/applications-rp:pr-func46b78d7472`
* dynamic-rp test image location: `ghcr.io/radius-project/dev/dynamic-rp:pr-func46b78d7472`
* controller test image location: `ghcr.io/radius-project/dev/controller:pr-func46b78d7472`
* ucp test image location: `ghcr.io/radius-project/dev/ucpd:pr-func46b78d7472`
* deployment-engine test image location: `ghcr.io/radius-project/deployment-engine:latest`

</details>

## Test Status
:hourglass: Building Radius and pushing container images for functional tests...
:white_check_mark: Container images build succeeded
:hourglass: Publishing Bicep Recipes for functional tests...
:white_check_mark: Recipe publishing succeeded
:hourglass: Starting corerp-cloud functional tests...
:hourglass: Starting ucp-cloud functional tests...
:white_check_mark: corerp-cloud functional tests succeeded
:white_check_mark: ucp-cloud functional tests succeeded
<!-- Sticky Pull Request Commentteststatus-14410783757 -->
author_association: NONE
user: login: radius-functional-tests[bot]
created_at: 2025-04-17T16:31:46Z
body: ## Radius functional test overview

:mag: **[Go to test action run](https://github.com/radius-project/radius/actions/runs/14520383210)**

| Name | Value |
|------|-------|
|**Repository** | radius-project/radius |
|**Commit ref** | 7dda05217f69f8b04500d3557cb084bbf00a526b |
|**Unique ID** | funce366ef8a8c |
|**Image tag** | pr-funce366ef8a8c |

<details>
<summary> Click here to see the list of tools in the current test run</summary>

* gotestsum 1.12.0
* KinD: v0.20.0
* Dapr: 
* Azure KeyVault CSI driver: 1.4.2
* Azure Workload identity webhook: 1.3.0
* Bicep recipe location `ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-funce366ef8a8c`
* Terraform recipe location `http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip` (in cluster)
* applications-rp test image location: `ghcr.io/radius-project/dev/applications-rp:pr-funce366ef8a8c`
* dynamic-rp test image location: `ghcr.io/radius-project/dev/dynamic-rp:pr-funce366ef8a8c`
* controller test image location: `ghcr.io/radius-project/dev/controller:pr-funce366ef8a8c`
* ucp test image location: `ghcr.io/radius-project/dev/ucpd:pr-funce366ef8a8c`
* deployment-engine test image location: `ghcr.io/radius-project/deployment-engine:latest`

</details>

## Test Status
:hourglass: Building Radius and pushing container images for functional tests...
:white_check_mark: Container images build succeeded
:hourglass: Publishing Bicep Recipes for functional tests...
:white_check_mark: Recipe publishing succeeded
:hourglass: Starting ucp-cloud functional tests...
:hourglass: Starting corerp-cloud functional tests...
:white_check_mark: ucp-cloud functional tests succeeded
:white_check_mark: corerp-cloud functional tests succeeded
<!-- Sticky Pull Request Commentteststatus-14520383210 -->
author_association: NONE