Repository: kubernetes-sigs/kueue

Issue:
Flaky E2E Test: StatefulSet integration when StatefulSet created should allow to change queue name if ReadyReplicas=0
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!

If the matter is security related, please disclose it privately via https://kubernetes.io/security/
-->


**What happened**:

End To End Suite: kindest/node:v1.30.10: [It] StatefulSet integration when StatefulSet created should allow to change queue name if ReadyReplicas=0

```
{Timed out after 45.001s.
The function passed to Eventually failed at /home/prow/go/src/sigs.k8s.io/kueue/test/e2e/singlecluster/statefulset_test.go:375 with:
Expected
    <int32>: 0
to equal
    <int32>: 3 failed [FAILED] Timed out after 45.001s.
The function passed to Eventually failed at /home/prow/go/src/sigs.k8s.io/kueue/test/e2e/singlecluster/statefulset_test.go:375 with:
Expected
    <int32>: 0
to equal
    <int32>: 3
In [It] at: /home/prow/go/src/sigs.k8s.io/kueue/test/e2e/singlecluster/statefulset_test.go:376 @ 04/18/25 07:24:31.271
}
```

**What you expected to happen**:
No errors

**How to reproduce it (as minimally and precisely as possible)**:

https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_kueue/5030/pull-kueue-test-e2e-main-1-30/1913127191198044160

**Anything else we need to know?**:

**Environment**:
- Kubernetes version (use `kubectl version`):
- Kueue version (use `git describe --tags --dirty --always`):
- Cloud provider or hardware configuration:
- OS (e.g: `cat /etc/os-release`):
- Kernel (e.g. `uname -a`):
- Install tools:
- Others:

Hints from issue comments:
@mbobrovskyi: /kind flake@mimowo: happend again recently https://prow.k8s.io/view/gs/kubernetes-ci-logs/logs/periodic-kueue-test-e2e-main-1-31/1925093654561558528@sohankunkerkar: Have we considered using `util.VeryLongTimeout` in this case? AFAIK, changing the queue name triggers a rolling update in the StatefulSet, as the webhook updates pod labels to ensure correct scheduling. This causes each pod to terminate and restart sequentially, which can take significantly longer than the current (`util.Longtimeout`) timeout allows.@mimowo: @sohankunkerkar I'm sure open to extending the timeout. Do you have some evidence, looking into the logs which could support the hypothesis that extending timeout would help? If so, I'm happy to accept the PR.@sohankunkerkar: Yeah, this issue surfaced during [this change](https://github.com/openshift/kueue-operator/pull/441#discussion_r2207285597) to the Kueue Operator, where we run upstream Kueue e2e tests in CI. After modifying the leader election configuration to use longer lease durations, the test started [failing](https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_kueue-operator/441/pull-ci-openshift-kueue-operator-release-1.0-test-e2e-4-19/1945360009701363712/artifacts/test-e2e-4-19/e2e-kueue-upstream/build-log.txt) because it incorrectly assumed the controller would be immediately functional once the deployment was marked as available. However, in reality, the leader election was still in progress, and the controller hadn’t become active yet.@mimowo: TBH, it does not feel related, we already wait for Kueue manager to be Available before running e2e tests.

I looked at the logs of the build https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_kueue/5030/pull-kueue-test-e2e-main-1-30/1913127191198044160

and the namespace for which the test failed was `sts-e2e-ljgzt` with the timeline of events:
```
  [1mSTEP:[0m Create StatefulSet [38;5;243m@ 04/18/25 07:23:45.216[0m
  [1mSTEP:[0m Checking that replicas is not ready [38;5;243m@ 04/18/25 07:23:45.251[0m
  [1mSTEP:[0m Update queue name [38;5;243m@ 04/18/25 07:23:46.252[0m
  [1mSTEP:[0m Waiting for replicas is ready [38;5;243m@ 04/18/25 07:23:46.27[0m
```
now, these are the logs of the Kueue manager in this time:

```
2025-04-18T07:23:45.212985235Z stderr F 2025-04-18T07:23:45.212782864Z	LEVEL(-2)	localqueue-reconciler	core/localqueue_controller.go:140	LocalQueue create event	{"localQueue": {"name":"sts-lq","namespace":"sts-e2e-ljgzt"}}
2025-04-18T07:23:45.215122602Z stderr F 2025-04-18T07:23:45.214952561Z	LEVEL(-2)	cluster-queue-reconciler	core/clusterqueue_controller.go:356	ClusterQueue update event	{"clusterQueue": {"name":"sts-cq"}}
2025-04-18T07:23:45.233477639Z stderr F 2025-04-18T07:23:45.233287367Z	LEVEL(-2)	localqueue-reconciler	core/localqueue_controller.go:173	Queue update event	{"localQueue": {"name":"sts-lq","namespace":"sts-e2e-ljgzt"}}
2025-04-18T07:23:45.311003525Z stderr F 2025-04-18T07:23:45.310514781Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:601	Workload create event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending"}
2025-04-18T07:23:45.311026755Z stderr F 2025-04-18T07:23:45.310628612Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:613	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:23:45.330689491Z stderr F 2025-04-18T07:23:45.330444059Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending"}
2025-04-18T07:23:45.330715562Z stderr F 2025-04-18T07:23:45.330594431Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:707	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:23:45.360205616Z stderr F 2025-04-18T07:23:45.359839074Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending"}
2025-04-18T07:23:45.360273227Z stderr F 2025-04-18T07:23:45.359962605Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:707	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:23:46.370037469Z stderr F 2025-04-18T07:23:46.368645918Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending"}
2025-04-18T07:23:46.370074289Z stderr F 2025-04-18T07:23:46.368744408Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:707	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq-invalid", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:23:46.404589184Z stderr F 2025-04-18T07:23:46.403579226Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending", "prevQueue": "sts-lq-invalid"}
2025-04-18T07:23:46.409785995Z stderr F 2025-04-18T07:23:46.409536383Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:631	Workload delete event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending"}
2025-04-18T07:24:31.289716957Z stderr F 2025-04-18T07:24:31.289509215Z	LEVEL(-2)	localqueue-reconciler	core/localqueue_controller.go:165	LocalQueue delete event	{"localQueue": {"name":"sts-lq","namespace":"sts-e2e-ljgzt"}}
2025-04-18T07:24:31.322200867Z stderr F 2025-04-18T07:24:31.321954415Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:601	Workload create event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending"}
2025-04-18T07:24:31.322229017Z stderr F 2025-04-18T07:24:31.322085786Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:613	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:24:31.322246277Z stderr F 2025-04-18T07:24:31.322122026Z	LEVEL(-2)	cluster-queue-reconciler	core/clusterqueue_controller.go:356	ClusterQueue update event	{"clusterQueue": {"name":"sts-cq"}}
2025-04-18T07:24:31.334769627Z stderr F 2025-04-18T07:24:31.334540635Z	LEVEL(-2)	cluster-queue-reconciler	core/clusterqueue_controller.go:343	ClusterQueue delete event	{"clusterQueue": {"name":"sts-cq"}}
2025-04-18T07:24:31.334791317Z stderr F 2025-04-18T07:24:31.334671426Z	LEVEL(-2)	cluster-queue-reconciler	core/clusterqueue_controller.go:349	Cleared resource metrics for deleted ClusterQueue.	{"clusterQueue": {"name":"sts-cq"}}
2025-04-18T07:24:31.33515214Z stderr F 2025-04-18T07:24:31.334946709Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending"}
2025-04-18T07:24:31.33518064Z stderr F 2025-04-18T07:24:31.335052899Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:707	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:24:31.349163132Z stderr F 2025-04-18T07:24:31.3489146Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:680	Workload update event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending"}
2025-04-18T07:24:31.349195592Z stderr F 2025-04-18T07:24:31.349045911Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:707	ignored an error for now	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending", "error": "localQueue doesn't exist or inactive"}
2025-04-18T07:24:31.352994783Z stderr F 2025-04-18T07:24:31.352795811Z	LEVEL(-2)	workload-reconciler	core/workload_controller.go:631	Workload delete event	{"workload": {"name":"statefulset-sts-18b6e","namespace":"sts-e2e-ljgzt"}, "queue": "sts-lq", "status": "pending"}
```
So, one outstanding thing is many "ignored an error for now" errors. I'm not yet totally sure, but these are likely the reason why the workload was not picked up after queue-name change, just because the CQ was not active while the workload was updated. 

This is actually already ticketed here: https://github.com/kubernetes-sigs/kueue/issues/5310, but for now the easiest fix for the flakes is to just ensure the CQ is active by calling `ExpectClusterQueuesToBeActive`, and maybe also ExpectLocalQueuesToBeActive. We use this tactic in many other tests.

@mimowo: Let me know if this makes sense and maybe you would also like to submit a PR fixing the flake along the lines.@sohankunkerkar: @mimowo Thanks for the apt reply. I think this is a much more targeted and accurate diagnosis than my initial leader election hypothesis. 

Solution patch:
diff --git a/cmd/kueueviz/backend/README.md b/cmd/kueueviz/backend/README.md
index 2e21bdb10..32db7809d 100644
--- a/cmd/kueueviz/backend/README.md
+++ b/cmd/kueueviz/backend/README.md
@@ -44,6 +44,7 @@ KUEUE_VIZ_PORT=8181 ./kueue_ws_app
 | Environment variables | Description              | Default value |
 | --------------------- | ------------------------ | ------------- |
 | `KUEUE_VIZ_PORT`      | Default application port | 8080          |
+| `GIN_MODE`            | Gin mode                 | debug         |
 
 ## Endpoints
 
@@ -63,3 +64,9 @@ KUEUE_VIZ_PORT=8181 ./kueue_ws_app
 | `/ws/cohort/{cohort_name}`                        | Streams updates for a specific cohort   |
 | `/ws/workload/{namespace}/{workload_name}`        | Streams updates for a specific workload |
 | `/ws/workload/{namespace}/{workload_name}/events` | Streams events for a specific workload  |
+
+### REST API
+
+| Endpoint                                          | Description                             |
+| ------------------------------------------------- | --------------------------------------- |
+| `/api/{resource_type}/{name}?namespace={namespace}&output={output_type}` | Returns content for a specific resource and output type |
diff --git a/cmd/kueueviz/backend/config/server.go b/cmd/kueueviz/backend/config/server.go
new file mode 100644
index 000000000..cdf8b6581
--- /dev/null
+++ b/cmd/kueueviz/backend/config/server.go
@@ -0,0 +1,81 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package config
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/viper"
+	"kueueviz/middleware"
+
+	_ "net/http/pprof"
+)
+
+// ServerConfig holds server configuration
+type ServerConfig struct {
+	Port string
+}
+
+// NewServerConfig creates a new server configuration
+func NewServerConfig() *ServerConfig {
+	viper.AutomaticEnv()
+	viper.SetDefault("KUEUEVIZ_PORT", "8080")
+
+	return &ServerConfig{
+		Port: viper.GetString("KUEUEVIZ_PORT"),
+	}
+}
+
+// SetupPprof starts the pprof server in development mode
+func SetupPprof() {
+	if gin.Mode() != gin.ReleaseMode {
+		go func() {
+			log.Println("Starting pprof server on localhost:6060")
+			if err := http.ListenAndServe("localhost:6060", nil); err != nil {
+				log.Printf("Error starting pprof server: %v", err)
+			}
+		}()
+	}
+}
+
+// SetupGinEngine creates and configures the Gin engine with middleware
+func SetupGinEngine() (*gin.Engine, error) {
+	r := gin.New()
+	r.Use(gin.Logger())
+	r.Use(gin.Recovery())
+
+	// Configure CORS with environment-aware settings
+	corsMiddleware, err := middleware.SetupCORS()
+	if err != nil {
+		return nil, fmt.Errorf("error setting up CORS: %v", err)
+	}
+	r.Use(corsMiddleware)
+
+	if err := r.SetTrustedProxies(nil); err != nil {
+		return nil, fmt.Errorf("error setting trusted proxies: %v", err)
+	}
+
+	return r, nil
+}
+
+// GetServerAddress returns the formatted server address
+func (c *ServerConfig) GetServerAddress() string {
+	return fmt.Sprintf(":%s", c.Port)
+}
diff --git a/cmd/kueueviz/backend/go.mod b/cmd/kueueviz/backend/go.mod
index 7a0bc0520..54078a7fd 100644
--- a/cmd/kueueviz/backend/go.mod
+++ b/cmd/kueueviz/backend/go.mod
@@ -3,6 +3,7 @@ module kueueviz
 go 1.24.0
 
 require (
+	github.com/gin-contrib/cors v1.7.6
 	github.com/gin-gonic/gin v1.10.1
 	github.com/gorilla/websocket v1.5.3
 	k8s.io/api v0.32.7
@@ -23,54 +24,53 @@ require (
 )
 
 require (
-	github.com/bytedance/sonic v1.11.6 // indirect
-	github.com/bytedance/sonic/loader v0.1.1 // indirect
-	github.com/cloudwego/base64x v0.1.4 // indirect
-	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/bytedance/sonic v1.13.3 // indirect
+	github.com/bytedance/sonic/loader v0.2.4 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect
 	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
-	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.9 // indirect
+	github.com/gin-contrib/sse v1.1.0 // indirect
 	github.com/go-logr/logr v1.4.2 // indirect
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
 	github.com/go-openapi/jsonreference v0.20.2 // indirect
 	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.20.0 // indirect
-	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/go-playground/validator/v10 v10.26.0 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/gnostic-models v0.6.8 // indirect
-	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
 	github.com/spf13/viper v1.20.1
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
-	github.com/ugorji/go/codec v1.2.12 // indirect
+	github.com/ugorji/go/codec v1.3.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
-	golang.org/x/arch v0.8.0 // indirect
-	golang.org/x/crypto v0.36.0 // indirect
-	golang.org/x/net v0.38.0 // indirect
+	golang.org/x/arch v0.18.0 // indirect
+	golang.org/x/crypto v0.39.0 // indirect
+	golang.org/x/net v0.41.0 // indirect
 	golang.org/x/oauth2 v0.27.0 // indirect
-	golang.org/x/sys v0.31.0 // indirect
-	golang.org/x/term v0.30.0 // indirect
-	golang.org/x/text v0.23.0 // indirect
+	golang.org/x/sys v0.33.0 // indirect
+	golang.org/x/term v0.32.0 // indirect
+	golang.org/x/text v0.26.0 // indirect
 	golang.org/x/time v0.8.0 // indirect
-	google.golang.org/protobuf v1.36.1 // indirect
+	google.golang.org/protobuf v1.36.6 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -79,5 +79,5 @@ require (
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
 	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
-	sigs.k8s.io/yaml v1.4.0 // indirect
+	sigs.k8s.io/yaml v1.4.0
 )
diff --git a/cmd/kueueviz/backend/go.sum b/cmd/kueueviz/backend/go.sum
index c757004f7..5b45ff55c 100644
--- a/cmd/kueueviz/backend/go.sum
+++ b/cmd/kueueviz/backend/go.sum
@@ -1,10 +1,10 @@
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
+github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
-github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
-github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
-github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
+github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -19,10 +19,12 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
 github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
-github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
+github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
+github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
 github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
 github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
@@ -41,14 +43,14 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
-github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
+github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
 github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -56,8 +58,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
 github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -74,8 +76,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
-github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -101,8 +103,8 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM
 github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
 github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
 github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
-github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
-github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -136,8 +138,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
-github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
-github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
+github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -146,22 +148,21 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
 go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
-golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
+golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
-golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
-golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
+golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -170,30 +171,29 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
-golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
 golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
 golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
-google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -217,7 +217,6 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
diff --git a/cmd/kueueviz/backend/handlers/api_handlers.go b/cmd/kueueviz/backend/handlers/api_handlers.go
new file mode 100644
index 000000000..19407ef51
--- /dev/null
+++ b/cmd/kueueviz/backend/handlers/api_handlers.go
@@ -0,0 +1,96 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package handlers
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/dynamic"
+	"sigs.k8s.io/yaml"
+)
+
+// ResourceResponse represents the response structure for resource endpoints
+type ResourceResponse struct {
+	Name    string `json:"name"`
+	Type    string `json:"type"`
+	Content string `json:"content"`
+	Format  string `json:"format"`
+}
+
+var resourceGVRMap = map[string]schema.GroupVersionResource{
+	"workload":       WorkloadsGVR(),
+	"clusterqueue":   ClusterQueuesGVR(),
+	"localqueue":     LocalQueuesGVR(),
+	"resourceflavor": ResourceFlavorsGVR(),
+	"cohort":         CohortsGVR(),
+}
+
+func GetResource(dynamicClient dynamic.Interface) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		resourceType := c.Param("resourceType")
+		name := c.Param("name")
+		namespace := c.Query("namespace")
+		outputType := c.Query("output")
+
+		if outputType != "yaml" {
+			c.JSON(http.StatusBadRequest, gin.H{
+				"error": fmt.Sprintf("Unsupported output format: %s.", outputType),
+			})
+			return
+		}
+
+		gvr, ok := resourceGVRMap[resourceType]
+		if !ok {
+			c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Unsupported resource type: %s", resourceType)})
+			return
+		}
+
+		resource, fetchErr := func() (any, error) {
+			if namespace != "" {
+				return dynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
+			}
+			return dynamicClient.Resource(gvr).Get(context.TODO(), name, metav1.GetOptions{})
+		}()
+
+		if fetchErr != nil {
+			c.JSON(http.StatusNotFound, gin.H{"error": "Resource not found"})
+			return
+		}
+
+		// Convert to YAML
+		yamlBytes, err := yaml.Marshal(resource)
+		if err != nil {
+			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process resource"})
+			return
+		}
+		content := string(yamlBytes)
+
+		response := ResourceResponse{
+			Name:    name,
+			Type:    resourceType,
+			Content: content,
+			Format:  outputType,
+		}
+
+		c.JSON(http.StatusOK, response)
+	}
+}
diff --git a/cmd/kueueviz/backend/handlers/handlers.go b/cmd/kueueviz/backend/handlers/handlers.go
index 20c90d52b..71ef1372c 100644
--- a/cmd/kueueviz/backend/handlers/handlers.go
+++ b/cmd/kueueviz/backend/handlers/handlers.go
@@ -49,3 +49,8 @@ func InitializeWebSocketRoutes(router *gin.Engine, dynamicClient dynamic.Interfa
 	router.GET("/ws/resource-flavors", ResourceFlavorsWebSocketHandler(dynamicClient))
 	router.GET("/ws/resource-flavor/:flavor_name", ResourceFlavorDetailsWebSocketHandler(dynamicClient))
 }
+
+func InitializeAPIRoutes(router *gin.Engine, dynamicClient dynamic.Interface) {
+	// Generic API route
+	router.GET("/api/:resourceType/:name", GetResource(dynamicClient))
+}
diff --git a/cmd/kueueviz/backend/main.go b/cmd/kueueviz/backend/main.go
index aeab290b3..aa3ca18b9 100644
--- a/cmd/kueueviz/backend/main.go
+++ b/cmd/kueueviz/backend/main.go
@@ -17,42 +17,37 @@ limitations under the License.
 package main
 
 import (
-	"fmt"
 	"log"
-	"net/http"
 
-	"github.com/gin-gonic/gin"
-	"github.com/spf13/viper"
+	"kueueviz/config"
 	"kueueviz/handlers"
-
-	_ "net/http/pprof"
 )
 
 func main() {
-	viper.AutomaticEnv()
-	// Start pprof server for profiling
-	go func() {
-		log.Println("Starting pprof server on :6060")
-		if err := http.ListenAndServe("localhost:6060", nil); err != nil {
-			log.Fatalf("Error starting pprof server: %v", err)
-		}
-	}()
+	// Initialize server configuration
+	serverConfig := config.NewServerConfig()
+
+	// Setup pprof for development
+	config.SetupPprof()
 
+	// Create Kubernetes client
 	_, dynamicClient, err := createK8sClient()
 	if err != nil {
 		log.Fatalf("Error creating Kubernetes client: %v", err)
 	}
-	r := gin.New()
-	r.Use(gin.Logger())
 
-	if err := r.SetTrustedProxies(nil); err != nil {
-		log.Fatalf("Error setting trusted proxies: %v", err)
+	// Setup Gin engine with middleware
+	r, err := config.SetupGinEngine()
+	if err != nil {
+		log.Fatalf("Error setting up Gin engine: %v", err)
 	}
 
+	// Initialize routes
 	handlers.InitializeWebSocketRoutes(r, dynamicClient)
+	handlers.InitializeAPIRoutes(r, dynamicClient)
 
-	viper.SetDefault("KUEUEVIZ_PORT", "8080")
-	if err := r.Run(fmt.Sprintf(":%s", viper.GetString("KUEUEVIZ_PORT"))); err != nil {
+	// Start server
+	if err := r.Run(serverConfig.GetServerAddress()); err != nil {
 		log.Fatalf("Failed to start server: %v", err)
 	}
 }
diff --git a/cmd/kueueviz/backend/middleware/cors.go b/cmd/kueueviz/backend/middleware/cors.go
new file mode 100644
index 000000000..4ab594ba7
--- /dev/null
+++ b/cmd/kueueviz/backend/middleware/cors.go
@@ -0,0 +1,112 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package middleware
+
+import (
+	"errors"
+	"fmt"
+	"log"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/gin-contrib/cors"
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/viper"
+)
+
+// validateOrigin validates and sanitizes a CORS origin URL
+func validateOrigin(origin string) (string, bool) {
+	u, err := url.Parse(origin)
+	if err != nil {
+		return "", false
+	}
+
+	if u.Scheme != "http" && u.Scheme != "https" {
+		return "", false
+	}
+
+	if u.Hostname() == "" {
+		return "", false
+	}
+
+	cleanOrigin := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
+	return cleanOrigin, true
+}
+
+// ConfigureCORS sets up CORS configuration based on environment
+func ConfigureCORS() (cors.Config, error) {
+	config := cors.DefaultConfig()
+
+	// Get allowed origins from environment variable
+	allowedOriginsEnv := viper.GetString("KUEUEVIZ_ALLOWED_ORIGINS")
+	var allowedOrigins []string
+
+	if allowedOriginsEnv != "" {
+		// Split comma-separated origins and validate each
+		rawOrigins := strings.Split(allowedOriginsEnv, ",")
+		for _, origin := range rawOrigins {
+			origin = strings.TrimSpace(origin)
+			if cleanOrigin, valid := validateOrigin(origin); valid {
+				allowedOrigins = append(allowedOrigins, cleanOrigin)
+			} else {
+				log.Printf("Warning: Invalid origin '%s' rejected", origin)
+			}
+		}
+	} else {
+		// Default development origins (only in development mode)
+		if gin.Mode() != gin.ReleaseMode {
+			allowedOrigins = []string{
+				"http://localhost:3000",
+				"http://127.0.0.1:3000",
+			}
+			log.Println("KUEUEVIZ_ALLOWED_ORIGINS not set, using default development origins")
+		} else {
+			log.Println("Production mode: KUEUEVIZ_ALLOWED_ORIGINS must be explicitly set")
+		}
+	}
+
+	config.AllowOrigins = allowedOrigins
+	config.AllowMethods = []string{"GET", "HEAD", "OPTIONS"}
+	config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"}
+
+	if gin.Mode() == gin.ReleaseMode {
+		log.Println("Running in production mode, applying security settings")
+		config.AllowCredentials = false
+		config.MaxAge = 12 * time.Hour
+
+		log.Printf("CORS allowed origins: %v", config.AllowOrigins)
+
+		if len(allowedOrigins) == 0 {
+			return config, errors.New("production mode requires valid CORS origins to be configured")
+		}
+	} else {
+		log.Println("Running in development mode")
+		config.AllowCredentials = true
+		config.MaxAge = 5 * time.Minute
+	}
+	return config, nil
+}
+
+// SetupCORS returns a configured CORS middleware
+func SetupCORS() (gin.HandlerFunc, error) {
+	config, err := ConfigureCORS()
+	if err != nil {
+		return nil, err
+	}
+	return cors.New(config), nil
+}
diff --git a/cmd/kueueviz/frontend/package-lock.json b/cmd/kueueviz/frontend/package-lock.json
index 923c62e9f..d8a612707 100644
--- a/cmd/kueueviz/frontend/package-lock.json
+++ b/cmd/kueueviz/frontend/package-lock.json
@@ -13,7 +13,9 @@
         "@emotion/styled": "^11.14.1",
         "@mui/icons-material": "^7.2.0",
         "@mui/material": "^7.2.0",
+        "ace-builds": "^1.43.2",
         "react": "^19.1.0",
+        "react-ace": "^14.0.1",
         "react-dom": "^19.0.0",
         "react-router-dom": "^7.7.0",
         "react-toastify": "^11.0.5"
@@ -1630,6 +1632,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/ace-builds": {
+      "version": "1.43.2",
+      "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.2.tgz",
+      "integrity": "sha512-3wzJUJX0RpMc03jo0V8Q3bSb/cKPnS7Nqqw8fVHsCCHweKMiTIxT3fP46EhjmVy6MCuxwP801ere+RW245phGw==",
+      "license": "BSD-3-Clause"
+    },
     "node_modules/ansi-regex": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -2005,6 +2013,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/diff-match-patch": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+      "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
+      "license": "Apache-2.0"
+    },
     "node_modules/dom-helpers": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@@ -2499,6 +2513,20 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+      "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isequal": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+      "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+      "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+      "license": "MIT"
+    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -2784,6 +2812,23 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-ace": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-14.0.1.tgz",
+      "integrity": "sha512-z6YAZ20PNf/FqmYEic//G/UK6uw0rn21g58ASgHJHl9rfE4nITQLqthr9rHMVQK4ezwohJbp2dGrZpkq979PYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ace-builds": "^1.36.3",
+        "diff-match-patch": "^1.0.5",
+        "lodash.get": "^4.4.2",
+        "lodash.isequal": "^4.5.0",
+        "prop-types": "^15.8.1"
+      },
+      "peerDependencies": {
+        "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
     "node_modules/react-dom": {
       "version": "19.1.0",
       "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
diff --git a/cmd/kueueviz/frontend/package.json b/cmd/kueueviz/frontend/package.json
index da6645a6c..b27e2cac0 100644
--- a/cmd/kueueviz/frontend/package.json
+++ b/cmd/kueueviz/frontend/package.json
@@ -17,7 +17,9 @@
     "@emotion/styled": "^11.14.1",
     "@mui/icons-material": "^7.2.0",
     "@mui/material": "^7.2.0",
+    "ace-builds": "^1.43.2",
     "react": "^19.1.0",
+    "react-ace": "^14.0.1",
     "react-dom": "^19.0.0",
     "react-router-dom": "^7.7.0",
     "react-toastify": "^11.0.5"
diff --git a/cmd/kueueviz/frontend/src/ClusterQueues.jsx b/cmd/kueueviz/frontend/src/ClusterQueues.jsx
index daca3117f..306bda541 100644
--- a/cmd/kueueviz/frontend/src/ClusterQueues.jsx
+++ b/cmd/kueueviz/frontend/src/ClusterQueues.jsx
@@ -17,9 +17,10 @@ limitations under the License.
 import React, { useState, useEffect } from 'react';
 import { Link } from 'react-router-dom';
 import useWebSocket from './useWebSocket';
-import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress } from '@mui/material';
+import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress, Box } from '@mui/material';
 import './App.css';
 import ErrorMessage from './ErrorMessage';
+import ViewYamlButton from './ViewYamlButton';
 
 const ClusterQueues = () => {
   const { data: clusterQueues, error } = useWebSocket('/ws/cluster-queues');
@@ -49,6 +50,7 @@ const ClusterQueues = () => {
                 <TableCell>Admitted Workloads</TableCell>
                 <TableCell>Pending Workloads</TableCell>
                 <TableCell>Reserving Workloads</TableCell>
+                <TableCell align="right">Actions</TableCell>
               </TableRow>
             </TableHead>
             <TableBody>
@@ -75,6 +77,14 @@ const ClusterQueues = () => {
                   <TableCell>{queue.admittedWorkloads ?? 'N/A'}</TableCell>
                   <TableCell>{queue.pendingWorkloads ?? 'N/A'}</TableCell>
                   <TableCell>{queue.reservingWorkloads ?? 'N/A'}</TableCell>
+                  <TableCell align="right">
+                    <Box display="flex" justifyContent="flex-end">
+                      <ViewYamlButton 
+                        resourceType="clusterqueue"
+                        resourceName={queue.name}
+                      />
+                    </Box>
+                  </TableCell>
                 </TableRow>
               ))}
             </TableBody>
diff --git a/cmd/kueueviz/frontend/src/Cohorts.jsx b/cmd/kueueviz/frontend/src/Cohorts.jsx
index 7eb5c4d6b..d26e637a2 100644
--- a/cmd/kueueviz/frontend/src/Cohorts.jsx
+++ b/cmd/kueueviz/frontend/src/Cohorts.jsx
@@ -16,10 +16,11 @@ limitations under the License.
 
 import React, { useState, useEffect } from 'react';
 import { Link } from 'react-router-dom';
-import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress } from '@mui/material';
+import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress, Box } from '@mui/material';
 import useWebSocket from './useWebSocket';
 import './App.css';
 import ErrorMessage from './ErrorMessage';
+import ViewYamlButton from './ViewYamlButton';
 
 const Cohorts = () => {
   const { data: cohorts, error } = useWebSocket('/ws/cohorts');
@@ -46,6 +47,7 @@ const Cohorts = () => {
                 <TableCell>Cohort Name</TableCell>
                 <TableCell>Number of Queues</TableCell>
                 <TableCell>Cluster Queue Name</TableCell>
+                <TableCell align="right">Actions</TableCell>
               </TableRow>
             </TableHead>
             <TableBody>
@@ -66,6 +68,16 @@ const Cohorts = () => {
                       <TableCell>
                         <Link to={`/cluster-queue/${queue.name}`}>{queue.name}</Link>
                       </TableCell>
+                      {index === 0 && (
+                        <TableCell rowSpan={cohort.clusterQueues.length} align="right">
+                          <Box display="flex" justifyContent="flex-end">
+                            <ViewYamlButton 
+                              resourceType="cohort"
+                              resourceName={cohort.name}
+                            />
+                          </Box>
+                        </TableCell>
+                      )}
                     </TableRow>
                   ))
                 ) : (
@@ -75,6 +87,14 @@ const Cohorts = () => {
                     </TableCell>
                     <TableCell>{0}</TableCell>
                     <TableCell>No cluster queues found for this cohort.</TableCell>
+                    <TableCell align="right">
+                      <Box display="flex" justifyContent="flex-end">
+                        <ViewYamlButton 
+                          resourceType="cohort"
+                          resourceName={cohort.name}
+                        />
+                      </Box>
+                    </TableCell>
                   </TableRow>
                 )
               ))}
diff --git a/cmd/kueueviz/frontend/src/LocalQueues.jsx b/cmd/kueueviz/frontend/src/LocalQueues.jsx
index 2b7898a1a..f53289a8b 100644
--- a/cmd/kueueviz/frontend/src/LocalQueues.jsx
+++ b/cmd/kueueviz/frontend/src/LocalQueues.jsx
@@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { CircularProgress, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material';
+import { CircularProgress, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, Box } from '@mui/material';
 import React, { useEffect, useState } from 'react';
 import { Link } from 'react-router-dom';
 import useWebSocket from './useWebSocket';
 import './App.css';
 import ErrorMessage from './ErrorMessage';
+import ViewYamlButton from './ViewYamlButton';
 
 const LocalQueues = () => {
   const { data: localQueues, error } = useWebSocket('/ws/local-queues');
@@ -59,6 +60,7 @@ const LocalQueues = () => {
                 <TableCell>Admitted Workloads</TableCell>
                 <TableCell>Pending Workloads</TableCell>
                 <TableCell>Reserving Workloads</TableCell>
+                <TableCell align="right">Actions</TableCell>
               </TableRow>
             </TableHead>
             <TableBody>
@@ -81,6 +83,15 @@ const LocalQueues = () => {
                     <TableCell>{queue.status?.admittedWorkloads}</TableCell>
                     <TableCell>{queue.status?.pendingWorkloads}</TableCell>
                     <TableCell>{queue.status?.reservingWorkloads}</TableCell>
+                    <TableCell align="right">
+                      <Box display="flex" justifyContent="flex-end">
+                        <ViewYamlButton 
+                          resourceType="localqueue"
+                          resourceName={queue.name}
+                          namespace={queue.namespace}
+                        />
+                      </Box>
+                    </TableCell>
                   </TableRow>
                 ))
               ))}
diff --git a/cmd/kueueviz/frontend/src/ResourceFlavors.jsx b/cmd/kueueviz/frontend/src/ResourceFlavors.jsx
index eea61c341..fbbd7b79f 100644
--- a/cmd/kueueviz/frontend/src/ResourceFlavors.jsx
+++ b/cmd/kueueviz/frontend/src/ResourceFlavors.jsx
@@ -16,10 +16,11 @@ limitations under the License.
 
 import React, { useState, useEffect } from 'react';
 import { Link } from 'react-router-dom';
-import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress } from '@mui/material';
+import { Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, CircularProgress, Box } from '@mui/material';
 import useWebSocket from './useWebSocket';
 import './App.css';
 import ErrorMessage from './ErrorMessage';
+import ViewYamlButton from './ViewYamlButton';
 
 const ResourceFlavors = () => {
   const { data: flavors, error } = useWebSocket('/ws/resource-flavors');
@@ -45,6 +46,7 @@ const ResourceFlavors = () => {
               <TableRow>
                 <TableCell>Name</TableCell>
                 <TableCell>Details</TableCell>
+                <TableCell align="right">Actions</TableCell>
               </TableRow>
             </TableHead>
             <TableBody>
@@ -54,6 +56,14 @@ const ResourceFlavors = () => {
                     <Link to={`/resource-flavor/${flavor.name}`}>{flavor.name}</Link>
                   </TableCell>
                   <TableCell>{JSON.stringify(flavor.details)}</TableCell>
+                  <TableCell align="right">
+                    <Box display="flex" justifyContent="flex-end">
+                      <ViewYamlButton 
+                        resourceType="resourceflavor"
+                        resourceName={flavor.name}
+                      />
+                    </Box>
+                  </TableCell>
                 </TableRow>
               ))}
             </TableBody>
diff --git a/cmd/kueueviz/frontend/src/ViewYamlButton.jsx b/cmd/kueueviz/frontend/src/ViewYamlButton.jsx
new file mode 100644
index 000000000..77f3a1a62
--- /dev/null
+++ b/cmd/kueueviz/frontend/src/ViewYamlButton.jsx
@@ -0,0 +1,56 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React, { useState } from 'react';
+import { Button } from '@mui/material';
+import YamlViewer from './YamlViewer';
+
+const ViewYamlButton = ({ resourceType, resourceName, namespace = null }) => {
+  const [yamlViewerOpen, setYamlViewerOpen] = useState(false);
+
+  const handleViewYaml = (event) => {
+    event.stopPropagation();
+    setYamlViewerOpen(true);
+  };
+
+  const handleCloseYamlViewer = () => {
+    setYamlViewerOpen(false);
+  };
+
+  return (
+    <>
+      <Button
+        variant="text"
+        size="small"
+        color="primary"
+        onClick={handleViewYaml}
+        sx={{ textTransform: 'none' }}
+      >
+        View YAML
+      </Button>
+      
+      <YamlViewer
+        open={yamlViewerOpen}
+        onClose={handleCloseYamlViewer}
+        resourceType={resourceType}
+        resourceName={resourceName}
+        namespace={namespace}
+      />
+    </>
+  );
+};
+
+export default ViewYamlButton;
diff --git a/cmd/kueueviz/frontend/src/Workloads.jsx b/cmd/kueueviz/frontend/src/Workloads.jsx
index 14869fd6c..46a0814b6 100644
--- a/cmd/kueueviz/frontend/src/Workloads.jsx
+++ b/cmd/kueueviz/frontend/src/Workloads.jsx
@@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { CircularProgress, FormControl, InputLabel, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography } from '@mui/material';
+import { CircularProgress, FormControl, InputLabel, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography, Box } from '@mui/material';
 import React, { useEffect, useState } from 'react';
 import { Link } from 'react-router-dom';
 import 'react-toastify/dist/ReactToastify.css';
 import useWebSocket from './useWebSocket';
 import './App.css';
 import ErrorMessage from './ErrorMessage';
+import ViewYamlButton from './ViewYamlButton';
 
 const Workloads = () => {
   const [selectedNamespace, setSelectedNamespace] = useState('');
@@ -103,6 +104,7 @@ const Workloads = () => {
                 <TableCell>Preemption Reason</TableCell>
                 <TableCell>Priority</TableCell>
                 <TableCell>Priority Class Name</TableCell>
+                <TableCell align="right">Actions</TableCell>
               </TableRow>
             </TableHead>
             <TableBody>
@@ -135,10 +137,20 @@ const Workloads = () => {
                       </Tooltip>
                     </TableCell>
                     <TableCell><Link to={`/local-queue/${workload.metadata.namespace}/${workload.spec.queueName}`}>{workload.spec.queueName}</Link></TableCell>
+                    <TableCell>{workload.status?.conditions?.[0]?.type || 'Unknown'}</TableCell>
                     <TableCell>{workload.preemption?.preempted ? "Yes" : "No"}</TableCell>
                     <TableCell>{workload.preemption?.reason || "N/A"}</TableCell>
                     <TableCell>{workload.spec.priority}</TableCell>
                     <TableCell>{workload.spec.priorityClassName}</TableCell>
+                    <TableCell align="right">
+                      <Box display="flex" justifyContent="flex-end">
+                        <ViewYamlButton 
+                          resourceType="workload"
+                          resourceName={workload.metadata.name}
+                          namespace={workload.metadata.namespace}
+                        />
+                      </Box>
+                    </TableCell>
                   </TableRow>
                 ))
               ))}
diff --git a/cmd/kueueviz/frontend/src/YamlViewer.jsx b/cmd/kueueviz/frontend/src/YamlViewer.jsx
new file mode 100644
index 000000000..021faf89c
--- /dev/null
+++ b/cmd/kueueviz/frontend/src/YamlViewer.jsx
@@ -0,0 +1,188 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React, { useState, useEffect, useRef, useCallback } from 'react';
+import {
+  Dialog,
+  DialogTitle,
+  DialogContent,
+  Typography,
+  CircularProgress,
+  Box,
+  IconButton,
+} from '@mui/material';
+import {
+  Close as CloseIcon,
+} from '@mui/icons-material';
+import AceEditor from 'react-ace';
+import { buildResourceUrl } from './utils/urlHelper';
+
+// Import Ace Editor modes and themes
+import 'ace-builds/src-noconflict/mode-yaml';
+import 'ace-builds/src-noconflict/ext-searchbox';
+
+const useYamlFetcher = (open, resourceType, resourceName, namespace) => {
+  const [yamlContent, setYamlContent] = useState('');
+  const [loading, setLoading] = useState(false);
+  const [error, setError] = useState('');
+
+  const fetchYaml = useCallback(async () => {
+    if (!open || !resourceType || !resourceName) return;
+
+    setLoading(true);
+    setError('');
+    setYamlContent('');
+
+    try {
+      const apiUrl = buildResourceUrl(resourceType, resourceName, {
+        namespace,
+        output: 'yaml'
+      });
+      
+      const response = await fetch(apiUrl);
+
+      if (!response.ok) {
+        throw new Error(`Failed to fetch YAML: ${response.status} ${response.statusText}`);
+      }
+
+      const data = await response.json();
+      setYamlContent(data.content);
+    } catch (err) {
+      setError(err.message);
+    } finally {
+      setLoading(false);
+    }
+  }, [open, resourceType, resourceName, namespace]);
+
+  useEffect(() => {
+    fetchYaml();
+  }, [fetchYaml]);
+
+  return { yamlContent, loading, error, refetch: fetchYaml };
+};
+
+const YamlViewer = ({ open, onClose, resourceType, resourceName, namespace = null }) => {
+  const { yamlContent, loading, error } = useYamlFetcher(open, resourceType, resourceName, namespace);
+  const editorRef = useRef(null);
+
+  // Handle keyboard shortcuts
+  useEffect(() => {
+    const handleKeyDown = (event) => {
+      if ((event.metaKey || event.ctrlKey) && event.key === 'f') {
+        return;
+      }
+      
+      // ESC to close dialog
+      if (event.key === 'Escape') {
+        if (open) {
+          handleClose();
+        }
+      }
+    };
+
+    if (open) {
+      document.addEventListener('keydown', handleKeyDown);
+      return () => {
+        document.removeEventListener('keydown', handleKeyDown);
+      };
+    }
+  }, [open]);
+
+
+
+  const handleClose = () => {
+    onClose();
+  };
+
+
+  const getResourceTitle = () => {
+    const resourceTypeName = resourceType.charAt(0).toUpperCase() + resourceType.slice(1);
+    const namespaceStr = namespace ? ` (${namespace})` : '';
+    return `${resourceTypeName}: ${resourceName}${namespaceStr}`;
+  };
+
+
+  return (
+    <Dialog
+      open={open}
+      onClose={handleClose}
+      maxWidth="lg"
+      fullWidth
+    >
+      <DialogTitle>
+        <Box display="flex" justifyContent="space-between" alignItems="center">
+          <Typography variant="h6" sx={{ color: 'text.primary' }}>
+            {getResourceTitle()}
+          </Typography>
+          <IconButton onClick={handleClose} size="small" sx={{color: 'text.primary'}}>
+            <CloseIcon />
+          </IconButton>
+        </Box>
+      </DialogTitle>
+      
+      <DialogContent 
+        dividers 
+        sx={{ 
+          backgroundColor: 'background.default', 
+          padding: 0, 
+          display: 'flex', 
+          flexDirection: 'column',
+          height: '70vh'
+        }}
+      >
+        {loading && (
+          <Box display="flex" justifyContent="center" p={4}>
+            <CircularProgress />
+          </Box>
+        )}
+
+        {error && (
+          <Box p={2}>
+            <Typography color="error" variant="body2">
+              Error: {error}
+            </Typography>
+          </Box>
+        )}
+
+        {yamlContent && !loading && (
+          <AceEditor
+            ref={editorRef}
+            mode="yaml"
+            value={yamlContent}
+            readOnly={true}
+            showPrintMargin={false}
+            highlightActiveLine={false}
+            width="100%"
+            height="100%"
+            setOptions={{
+              useWorker: false,
+              showLineNumbers: true,
+              tabSize: 2,
+              wrap: false,
+              enableBasicAutocompletion: false,
+              enableLiveAutocompletion: false,
+              enableSnippets: false,
+              showFoldWidgets: true,
+            }}
+            enableSearchBox={true}
+          />
+        )}
+      </DialogContent>
+    </Dialog>
+  );
+};
+
+export default YamlViewer;
diff --git a/cmd/kueueviz/frontend/src/useWebSocket.js b/cmd/kueueviz/frontend/src/useWebSocket.js
index 3e6c7f48b..5334ae193 100644
--- a/cmd/kueueviz/frontend/src/useWebSocket.js
+++ b/cmd/kueueviz/frontend/src/useWebSocket.js
@@ -15,14 +15,12 @@ limitations under the License.
 */
 
 import { useEffect, useState } from 'react';
-import { env } from './env'
-
-const websocketURL = env.REACT_APP_WEBSOCKET_URL;
+import { buildWebSocketUrl } from './utils/urlHelper';
 
 const useWebSocket = (url) => {
   const [data, setData] = useState(null);
   const [error, setError] = useState(null);
-  const fullUrl = `${websocketURL}${url}`;
+  const fullUrl = buildWebSocketUrl(url);
   useEffect(() => {
     const ws = new WebSocket(fullUrl);
 
@@ -40,8 +38,6 @@ const useWebSocket = (url) => {
       console.error("WebSocket error:", err);
       // Log environment variables to the console
       console.log(`Backend URL: ${fullUrl}`);
-      console.log(`REACT_APP_WEBSOCKET_URL: ${env.REACT_APP_WEBSOCKET_URL}`);
-      console.log(`VITE_WEBSOCKET_URL: ${env.VITE_WEBSOCKET_URL}`);
 
       // Function to get all properties, including inherited ones
       const getAllProperties = (obj) => {
diff --git a/cmd/kueueviz/frontend/src/utils/urlHelper.js b/cmd/kueueviz/frontend/src/utils/urlHelper.js
new file mode 100644
index 000000000..13c7feb5e
--- /dev/null
+++ b/cmd/kueueviz/frontend/src/utils/urlHelper.js
@@ -0,0 +1,104 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { env } from "../env";
+
+/**
+ * Gets the backend WebSocket URL from environment variables
+ * @returns {string} The WebSocket URL
+ * @throws {Error} If no backend URL is configured
+ */
+export const getBackendWebSocketUrl = () => {
+  const backendUrl = env.REACT_APP_WEBSOCKET_URL || env.VITE_WEBSOCKET_URL;
+  if (!backendUrl) {
+    throw new Error(
+      "Backend URL is not configured. Please set REACT_APP_WEBSOCKET_URL or VITE_WEBSOCKET_URL."
+    );
+  }
+  return backendUrl;
+};
+
+/**
+ * Converts a WebSocket URL to HTTP/HTTPS URL
+ * @param {string} wsUrl - The WebSocket URL
+ * @returns {string} The HTTP/HTTPS URL
+ */
+export const convertWebSocketToHttp = (wsUrl) => {
+  return wsUrl.replace("ws://", "http://").replace("wss://", "https://");
+};
+
+/**
+ * Gets the backend HTTP URL for API calls
+ * @returns {string} The HTTP URL
+ * @throws {Error} If no backend URL is configured
+ */
+export const getBackendHttpUrl = () => {
+  const wsUrl = getBackendWebSocketUrl();
+  return convertWebSocketToHttp(wsUrl);
+};
+
+/**
+ * Builds a complete WebSocket URL with the given path
+ * @param {string} path - The WebSocket path (e.g., '/ws/workloads')
+ * @returns {string} The complete WebSocket URL
+ */
+export const buildWebSocketUrl = (path) => {
+  const baseUrl = getBackendWebSocketUrl();
+  return `${baseUrl}${path}`;
+};
+
+/**
+ * Builds a complete API URL with the given path and optional query parameters
+ * @param {string} path - The API path (e.g., '/api/workload/my-workload')
+ * @param {Object} params - Optional query parameters as key-value pairs
+ * @returns {string} The complete API URL
+ */
+export const buildApiUrl = (path, params = {}) => {
+  const baseUrl = getBackendHttpUrl();
+  const url = new URL(path, baseUrl);
+
+  Object.entries(params).forEach(([key, value]) => {
+    if (value !== null && value !== undefined) {
+      url.searchParams.append(key, value);
+    }
+  });
+
+  return url.toString();
+};
+
+/**
+ * Builds a resource API URL for YAML format
+ * @param {string} resourceType - The resource type (e.g., 'workload', 'clusterqueue')
+ * @param {string} resourceName - The resource name
+ * @param {Object} options - Optional parameters
+ * @param {string} options.namespace - The namespace (for namespaced resources)
+ * @param {string} options.output - The output format (currently only 'yaml' is supported)
+ * @returns {string} The complete resource API URL
+ */
+export const buildResourceUrl = (resourceType, resourceName, options = {}) => {
+  const { namespace, output } = options;
+  const path = `/api/${resourceType}/${resourceName}`;
+
+  const params = {};
+  if (namespace) {
+    params.namespace = namespace;
+  }
+  if (output) {
+    params.output = output;
+  }
+
+  return buildApiUrl(path, params);
+};



Test patch:
diff --git a/test/e2e/kueueviz/cypress/e2e/app.cy.js b/test/e2e/kueueviz/cypress/e2e/app.cy.js
index 9933e7cb5..54a9e4969 100644
--- a/test/e2e/kueueviz/cypress/e2e/app.cy.js
+++ b/test/e2e/kueueviz/cypress/e2e/app.cy.js
@@ -86,5 +86,26 @@ describe('Kueue Dashboard', () => {
     });
   })
 
+  it('should open and close YAML viewer modal on resource-flavors page', { defaultCommandTimeout: 15000 }, () => {
+    cy.visit('/resource-flavors')
+    
+    cy.get('table').should('exist')
+    
+    cy.contains('button', 'View YAML').first().click()
+    
+    cy.get('[role="dialog"]').should('be.visible')
+    
+    cy.get('[role="dialog"]').within(() => {
+      cy.get('h6').should('exist') 
+      cy.get('button').find('svg').should('exist')
+    })
+    
+    cy.get('[role="dialog"]').within(() => {
+      cy.get('button').find('svg').click()
+    })
+    
+    cy.get('[role="dialog"]').should('not.exist')
+  })
+
   // Add more test cases here as needed
 }) 
\ No newline at end of file
diff --git a/test/e2e/singlecluster/statefulset_test.go b/test/e2e/singlecluster/statefulset_test.go
index 5132d7f30..a8629a493 100644
--- a/test/e2e/singlecluster/statefulset_test.go
+++ b/test/e2e/singlecluster/statefulset_test.go
@@ -69,9 +69,11 @@ var _ = ginkgo.Describe("StatefulSet integration", func() {
 			}).
 			Obj()
 		util.MustCreate(ctx, k8sClient, cq)
+		util.ExpectClusterQueuesToBeActive(ctx, k8sClient, cq)
 
 		lq = testing.MakeLocalQueue(localQueueName, ns.Name).ClusterQueue(cq.Name).Obj()
 		util.MustCreate(ctx, k8sClient, lq)
+		util.ExpectLocalQueuesToBeActive(ctx, k8sClient, lq)
 	})
 	ginkgo.AfterEach(func() {
 		gomega.Expect(util.DeleteNamespace(ctx, k8sClient, ns)).To(gomega.Succeed())