Repository: kubernetes-sigs/kro

Issue:
RGD - Failing on field type parsing
### Description

<!-- Please craft a detailed description of the documentation issue or suggestion -->
<!-- Below are some suggestions but feel free to modify or remove them -->

**Observed Behavior**: Defining a resource with below resource definition fails on: "expected number type for path renotifyInterval, got int64"
`optionsList:
- monitorOptions:
  - renotifyInterval: 0
`

**Expected Behavior**: The resource definition should be parsed successfully

**Reproduction Steps** (Please include `ResourceGraphDefinition` and `Instances` files): The following GRD when applied should the mentioned error on its description:
`
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: synthtestwithslo.kro.run
spec:
  schema:
    apiVersion: v1alpha1
    kind: SynthTestWithSlo
    spec:
      name: string
      namespace: string | default=default
  resources:
  - id: test
    template:
      apiVersion: synthetics.datadog.upbound.io/v1alpha1
      kind: Test
      metadata:
        name: ${schema.spec.name}-test
        labels:
          app.kubernetes.io/name: ${schema.spec.name}
      spec:
        forProvider:
          assertion:
          - operator: lessThan
            target: "1000"
            type: responseTime
          locations:
          - pl:platform-private-location-7997d828d18ffe96cbd5c7ba76f198da
          message: "test for xyz"
          name: my-test
          requestHeaders: {}
          requestDefinition:
          - method: GET
            url: https://www.google.com
          tags:
          - service:MyService
          status: live
          subtype: http
          type: api
          optionsList:
          - monitorOptions:
            - renotifyInterval: 0
            retry:
            - count: 3
              interval: 60
            tickEvery: 60
            monitorPriority: 2
`

**Versions**:
- kro version: 0.2.1
- Kubernetes Version (`kubectl version`):
Client Version: v1.30.1
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.31.4-eks-2d5f260

**Involved Controllers**:
- Controller URLs and Versions (if applicable): crossplane DataDog provider. https://github.com/upbound/provider-datadog

**Error Logs** (if applicable)**:
```
Paste any relevant logs here
2025-02-08T16:23:58.792Z	DEBUG	controller.resourceGraphDefinition	processing reconciliation error	{"resourcegraphdefinition": {"name":"synthtestwithslo.kro.run"}, "error": "failed to build resource \"test\": failed to extract CEL expressions from schema for resource test: expected number type for path spec.forProvider.optionsList[0].monitorOptions[0].renotifyInterval, got int64"}
```

* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request
* Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
* If you are interested in working on this issue or have submitted a pull request, please leave a comment


### Which option describes the most your issue?

_No response_

Hints from issue comments:
@a-hilaly: Fixed in #284 thanks @khalilj-appsflyer !

Solution patch:
diff --git a/.github/workflows/build-push-image.yaml b/.github/workflows/build-push-image.yaml
index eef3e74..69cdc91 100644
--- a/.github/workflows/build-push-image.yaml
+++ b/.github/workflows/build-push-image.yaml
@@ -24,7 +24,7 @@ jobs:
       - name: Set up Go
         uses: actions/setup-go@v4
         with:
-          go-version: '1.23'
+          go-version: '1.24'
 
       - name: Install Dependencies
         run: |
diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml
index e1c3560..9fb8a02 100644
--- a/.github/workflows/golangci-lint.yaml
+++ b/.github/workflows/golangci-lint.yaml
@@ -24,5 +24,5 @@ jobs:
         uses: golangci/golangci-lint-action@v6
         with:
           args: --timeout=5m
-          version: v1.60
-          only-new-issues: true
\ No newline at end of file
+          version: v1.64
+          only-new-issues: true
diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml
index 3a62659..90dfb08 100644
--- a/.github/workflows/unit-tests.yaml
+++ b/.github/workflows/unit-tests.yaml
@@ -10,7 +10,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go-version: [ '1.22', '1.23' ]
+        go-version: [ '1.24' ]
 
     steps:
       - uses: actions/checkout@v3
diff --git a/.golangci.yml b/.golangci.yml
index 7e3a760..ad91e0d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,5 +1,5 @@
 run:
-  deadline: 5m
+  timeout: 5m
   allow-parallel-runners: true
 
 issues:
diff --git a/.ko.yaml b/.ko.yaml
index d8d3bd7..2d850e3 100644
--- a/.ko.yaml
+++ b/.ko.yaml
@@ -1,6 +1,4 @@
-# gently copied from https://github.com/aws/karpenter-provider-aws/blob/main/.ko.yaml
-
-defaultBaseImage: public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base
+defaultBaseImage: gcr.io/distroless/static
 defaultPlatforms:
   - linux/arm64
   - linux/amd64
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aba9347..dea0ddb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,6 @@ documentation, we greatly value feedback and contributions from our community.
 Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
 information to effectively respond to your bug report or contribution.
 
-
 ## Reporting Bugs/Feature Requests
 
 We welcome you to use the GitHub issue tracker to report bugs or suggest features.
@@ -39,125 +38,9 @@ To send us a pull request, please:
 GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
 [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
 
-## Setting Up a Local Development Environment
-
-By following the steps for [externally running a controller](#running-the-controller-external-to-the-cluster) or 
-[running the controller inside a `KinD` cluster](#running-the-controller-inside-a-kind-cluster-with-ko), you can set up 
-a local environment to test your contributions before submitting a pull request.
-
-### Running the controller external to the cluster
-
-To test and run the project with your local changes, follow these steps to set up a development environment:
-
-1. Install Dependencies: Ensure you have the necessary dependencies installed, including:
-    - [Go](https://golang.org/doc/install) (version specified in `go.mod`).
-    - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) for interacting with Kubernetes clusters.
-    - A local Kubernetes cluster such as [kind](https://kind.sigs.k8s.io/).
-
-2. Create a Local Kubernetes Cluster: If you don't already have a cluster, create one with your preferred tool. For example, with `kind`:
-    ```bash
-    kind create cluster
-    ```
-
-3. Install the Custom Resource Definitions (CRDs): Apply the latest CRDs to your cluster:
-    ```bash
-    make generate
-    kubectl apply -k ./config/crd
-    ```
-
-4. Run the kro Controller Locally: Execute the controller with your changes:
-    ```bash
-    go run ./cmd/controller --log-level 2
-    ```
-    This will connect to the default Kubernetes context in your local kubeconfig (`~/.kube/config`). Ensure the context is pointing to your local cluster.
-
-### Running the controller inside a [`KinD`][kind] cluster with [`ko`][ko]
-
-[ko]: https://ko.build
-[kind]: https://kind.sigs.k8s.io/
-
-1. Create a `KinD` cluster.
-
-   ```sh
-   kind create cluster
-   ```
-
-2. Create the `kro-system` namespace.
-
-   ```sh
-   kubectl create namespace kro-system
-   ```
-
-3. Set the `KO_DOCKER_REPO` env var.
-
-   ```sh
-   export KO_DOCKER_REPO=kind.local
-   ```
-
-   > _Note_, if not using the default kind cluster name, set KIND_CLUSTER_NAME
-
-   ```sh
-   export KIND_CLUSTER_NAME=my-other-cluster
-   ```
-4. Apply the Kro CRDs.
-
-   ```sh
-   make manifests
-   kubectl apply -f ./helm/crds
-   ```
-
-5. Render and apply the local helm chart.
-
-   ```sh
-    helm template kro ./helm \
-      --namespace kro-system \
-      --set image.pullPolicy=Never \
-      --set image.ko=true | ko apply -f -
-    ```
-
-### Dev Environment Hello World
-
-1. Create a `NoOp` ResourceGraph using the `ResourceGraphDefinition`.
-
-   ```sh
-   kubectl apply -f - <<EOF
-   apiVersion: kro.run/v1alpha1
-   kind: ResourceGraphDefinition
-   metadata:
-     name: noop
-   spec:
-     schema:
-       apiVersion: v1alpha1
-       kind: NoOp
-       spec: {}
-       status: {}
-     resources: []
-   EOF
-   ```
-
-   Inspect that the `ResourceGraphDefinition` was created, and also the newly created CRD `NoOp`.
-
-   ```sh
-   kubectl get ResourceGraphDefinition noop
-   kubectl get crds | grep noops
-   ```
-
-3. Create an instance of the new `NoOp` kind.
+## Development setup
 
-   ```sh
-   kubectl apply -f - <<EOF
-   apiVersion: kro.run/v1alpha1
-   kind: NoOp
-   metadata:
-     name: demo
-   EOF
-   ```
-
-   And inspect the new instance,
-
-   ```shell
-   kubectl get noops -oyaml
-   ```
+Setup the [local enviroment](docs/developer-getting-started.md) to build and test the code locally.
 
 ## Finding contributions to work on
 Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
diff --git a/Makefile b/Makefile
index 8e928d2..b87899b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,13 @@
 
-AWS_ACCOUNT_ID ?= $(shell aws sts get-caller-identity --query Account --output text)
-AWS_REGION ?= us-west-2
 RELEASE_VERSION ?= dev-$(shell git rev-parse --short HEAD)
 OCI_REPO ?= ghcr.io/kro-run/kro
 
-CONTROLLER_IMAGE ?= ${OCI_REPO}/controller:${RELEASE_VERSION}
 HELM_IMAGE ?= ${OCI_REPO}
 KO_DOCKER_REPO ?= ${OCI_REPO}/kro
 
 KOCACHE ?= ~/.ko
 KO_PUSH ?= true
+export KIND_CLUSTER_NAME ?= kro
 
 WITH_GOFLAGS = GOFLAGS="$(GOFLAGS)"
 
@@ -89,7 +87,7 @@ else
 endif
 
 GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
-GOLANGCI_LINT_VERSION ?= v1.54.2
+GOLANGCI_LINT_VERSION ?= v1.64.5
 golangci-lint:
 	@[ -f $(GOLANGCI_LINT) ] || { \
 	set -e ;\
@@ -151,14 +149,26 @@ $(LOCALBIN):
 
 ## Tool Binaries
 KUBECTL ?= kubectl
+KIND ?= kind
 KUSTOMIZE ?= $(LOCALBIN)/kustomize
+KO ?= $(LOCALBIN)/ko
 CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
 ENVTEST ?= $(LOCALBIN)/setup-envtest
 
 ## Tool Versions
+KO_VERSION ?= v0.17.1
 KUSTOMIZE_VERSION ?= v5.2.1
 CONTROLLER_TOOLS_VERSION ?= v0.16.2
 
+.PHONY: ko
+ko: $(KO) ## Download ko locally if necessary. If wrong version is installed, it will be removed before downloading.
+$(KO): $(LOCALBIN)
+	@if test -x $(LOCALBIN)/ko && ! $(LOCALBIN)/ko version | grep -q $(KO_VERSION); then \
+		echo "$(LOCALBIN)/ko version is not expected $(KO_VERSION). Removing it before installing."; \
+		rm -rf $(LOCALBIN)/ko; \
+	fi
+	test -s $(LOCALBIN)/ko || GOBIN=$(LOCALBIN) GO111MODULE=on go install github.com/google/ko@$(KO_VERSION)
+
 .PHONY: kustomize
 kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
 $(KUSTOMIZE): $(LOCALBIN)
@@ -175,15 +185,17 @@ $(CONTROLLER_GEN): $(LOCALBIN)
 	GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
 
 .PHONY: image
-build-image: ## Build the kro controller images using ko build
-	$(WITH_GOFLAGS) KOCACHE=$(KOCACHE) \
-		ko build --bare github.com/kro-run/kro/cmd/controller \
+build-image: ko ## Build the kro controller images using ko build
+	echo "Building kro image $(RELEASE_VERSION).."
+	$(WITH_GOFLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(KO_DOCKER_REPO) \
+		$(KO) build --bare github.com/kro-run/kro/cmd/controller \
+		--local\
 		--push=false --tags ${RELEASE_VERSION} --sbom=none
 
 .PHONY: publish
-publish-image: ## Publish the kro controller images to ghcr.io
+publish-image: ko ## Publish the kro controller images to ghcr.io
 	$(WITH_GOFLAGS) KOCACHE=$(KOCACHE) \
-		ko publish --bare github.com/kro-run/kro/cmd/controller \
+		$(KO) publish --bare github.com/kro-run/kro/cmd/controller \
 		--tags ${RELEASE_VERSION} --sbom=none
 
 .PHONY: package-helm
@@ -199,4 +211,31 @@ publish-helm: ## Helm publish
 	helm push ./kro-${RELEASE_VERSION}.tgz oci://${HELM_IMAGE}
 
 .PHONY:
-release: build-image publish-image package-helm publish-helm
\ No newline at end of file
+release: build-image publish-image package-helm publish-helm
+
+##@ Deployment
+
+ifndef ignore-not-found
+  ignore-not-found = true
+endif
+
+.PHONY: install
+install: manifests ## Install CRDs into the K8s cluster specified in ~/.kube/config.
+	$(KUBECTL) apply -f ./helm/crds
+
+.PHONY: uninstall
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config
+	$(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f ./helm/crds
+
+.PHONY: deploy-kind
+deploy-kind: export KO_DOCKER_REPO=kind.local
+deploy-kind:
+	$(KIND) delete clusters ${KIND_CLUSTER_NAME} || true
+	$(KIND) create cluster --name ${KIND_CLUSTER_NAME}
+	$(KUBECTL) --context kind-$(KIND_CLUSTER_NAME) create namespace kro-system
+	make install
+	# This generates deployment with ko://... used in image. 
+	# ko then intercepts it builds image, pushes to kind node, replaces the image in deployment and applies it
+	helm template kro ./helm --namespace kro-system --set image.pullPolicy=Never --set image.ko=true | $(KO) apply -f -
+	kubectl wait --for=condition=ready --timeout=1m pod -n kro-system -l app.kubernetes.io/component=controller
+	$(KUBECTL) --context kind-${KIND_CLUSTER_NAME} get pods -A
diff --git a/README.md b/README.md
index ab0ef19..903031d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,13 @@
 # kro | Kube Resource Orchestrator
 
+
+[![Go Report Card](https://goreportcard.com/badge/github.com/kro-run/kro)](https://goreportcard.com/report/github.com/kro-run/kro)
+[![unit tests](https://github.com/kro-run/kro/actions/workflows/unit-tests.yaml/badge.svg)](https://github.com/kro-run/kro/actions/workflows/unit-tests.yaml)
+![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/kro-run/Kro)
+![GitHub License](https://img.shields.io/github/license/kro-run/kro)
+[![Build and Publish](https://github.com/kro-run/kro/actions/workflows/build-push-image.yaml/badge.svg?branch=main)](https://github.com/kro-run/kro/actions/workflows/build-push-image.yaml)
+![GitHub Repo stars](https://img.shields.io/github/stars/kro-run/kro)
+
 This project aims to simplify the creation and management of complex custom resources for Kubernetes.
 
 Kube Resource Orchestrator (**kro**) helps you to define complex multi-resource constructs as reusable components in your applications and systems. It does this by providing a Kubernetes-native, vendor agnostic way to define groupings of Kubernetes resources. 
@@ -72,6 +80,18 @@ Development and discussion is coordinated in the [Kubernetes Slack (invite link)
 [k8s-slack]: https://communityinviter.com/apps/kubernetes/community
 [kro-channel]: https://kubernetes.slack.com/archives/C081TMY9D6Y
 
+Please join our community meeting.
+* Every other Wednesday at 9AM PT (Pacific Time). [Convert to local timezone][tz-help] 
+* Agenda: [Public doc][agenda]
+* Join us: [Zoom meeting][zoom]
+* Community meeting recordings:  [YouTube channel][youtube]
+
+
+[tz-help]: http://www.thetimezoneconverter.com/?t=9%3A00&tz=PT%20%28Pacific%20Time%29
+[agenda]: https://docs.google.com/document/d/1GqeHcBlOw6ozo-qS4TLdXSi5qUn88QU6dwdq0GvxRz4/edit?tab=t.0
+[zoom]: https://us06web.zoom.us/j/85388697226?pwd=9Xxz1F0FcNUq8zFGrsRqkHMhFZTpuj.1
+[youtube]: https://www.youtube.com/channel/UCUlcI3NYq9ehl5wsdfbJzSA
+
 ## Security
 
 See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
diff --git a/api/v1alpha1/conditions.go b/api/v1alpha1/conditions.go
index ebabc9f..c251af3 100644
--- a/api/v1alpha1/conditions.go
+++ b/api/v1alpha1/conditions.go
@@ -15,13 +15,14 @@ package v1alpha1
 
 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"slices"
 )
 
 // ConditionType is a type of condition for a resource.
 type ConditionType string
 
 const (
-	// ResourceGraphDefinitionConditionTypeGraphSynced indicates the state of the directed
+	// ResourceGraphDefinitionConditionTypeGraphVerified indicates the state of the directed
 	// acyclic graph (DAG) that kro uses to manage the resources in a
 	// ResourceGraphDefinition.
 	ResourceGraphDefinitionConditionTypeGraphVerified ConditionType = "GraphVerified"
@@ -39,13 +40,13 @@ const (
 const (
 	InstanceConditionTypeReady ConditionType = "Ready"
 
-	// Creating Deleting Migrating
+	// InstanceConditionTypeProgressing used for Creating Deleting Migrating
 	InstanceConditionTypeProgressing ConditionType = "Progressing"
 
-	// Unexpected situation, behaviour, need human intervention
+	// InstanceConditionTypeDegraded used in unexpected situation, behaviour, need human intervention
 	InstanceConditionTypeDegraded ConditionType = "Degraded"
 
-	// Something is wrong but i'm gonna try again
+	// InstanceConditionTypeError used in something is wrong but i'm going to try again
 	InstanceConditionTypeError ConditionType = "Error"
 )
 
@@ -63,7 +64,7 @@ type Condition struct {
 	// The reason for the condition's last transition.
 	// +optional
 	Reason *string `json:"reason,omitempty"`
-	// A human readable message indicating details about the transition.
+	// A human-readable message indicating details about the transition.
 	// +optional
 	Message *string `json:"message,omitempty"`
 	// observedGeneration represents the .metadata.generation that the condition was set based upon.
@@ -85,15 +86,6 @@ func NewCondition(t ConditionType, status metav1.ConditionStatus, reason, messag
 	}
 }
 
-func GetCondition(conditions []Condition, t ConditionType) *Condition {
-	for _, c := range conditions {
-		if c.Type == t {
-			return &c
-		}
-	}
-	return nil
-}
-
 func SetCondition(conditions []Condition, condition Condition) []Condition {
 	for i, c := range conditions {
 		if c.Type == condition.Type {
@@ -105,5 +97,7 @@ func SetCondition(conditions []Condition, condition Condition) []Condition {
 }
 
 func HasCondition(conditions []Condition, t ConditionType) bool {
-	return GetCondition(conditions, t) != nil
+	return slices.ContainsFunc(conditions, func(c Condition) bool {
+		return c.Type == t
+	})
 }
diff --git a/cmd/controller/main.go b/cmd/controller/main.go
index 6d9d082..a99c12e 100644
--- a/cmd/controller/main.go
+++ b/cmd/controller/main.go
@@ -60,20 +60,22 @@ func (c customLevelEnabler) Enabled(lvl zapcore.Level) bool {
 }
 
 func main() {
-	var metricsAddr string
-	var enableLeaderElection bool
-	var probeAddr string
-	var allowCRDDeletion bool
-	var resourceGraphDefinitionConcurrentReconciles int
-	var dynamicControllerConcurrentReconciles int
-	// reconciler parameters
-	var resyncPeriod int
-	var queueMaxRetries int
-	var shutdownTimeout int
-	// var dynamicControllerDefaultResyncPeriod int
-	var logLevel int
-	var qps float64
-	var burst int
+	var (
+		metricsAddr                                 string
+		enableLeaderElection                        bool
+		probeAddr                                   string
+		allowCRDDeletion                            bool
+		resourceGraphDefinitionConcurrentReconciles int
+		dynamicControllerConcurrentReconciles       int
+		// reconciler parameters
+		resyncPeriod    int
+		queueMaxRetries int
+		shutdownTimeout int
+		// var dynamicControllerDefaultResyncPeriod int
+		logLevel int
+		qps      float64
+		burst    int
+	)
 
 	flag.StringVar(&metricsAddr, "metrics-bind-address", ":8078", "The address the metric endpoint binds to.")
 	flag.StringVar(&probeAddr, "health-probe-bind-address", ":8079", "The address the probe endpoint binds to.")
@@ -189,20 +191,27 @@ func main() {
 		os.Exit(1)
 	}
 
-	go dc.Run(context.Background())
+	go func() {
+		err := dc.Run(context.Background())
+		if err != nil {
+			setupLog.Error(err, "dynamic controller failed to run")
+		}
+	}()
 
 	//+kubebuilder:scaffold:builder
 
-	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+	if err = mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
 		setupLog.Error(err, "unable to set up health check")
 		os.Exit(1)
 	}
-	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+
+	if err = mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
 		setupLog.Error(err, "unable to set up ready check")
 		os.Exit(1)
 	}
 
 	ctx := ctrl.SetupSignalHandler()
+
 	go func() {
 		if err := mgr.Start(ctx); err != nil {
 			setupLog.Error(err, "problem running manager")
diff --git a/docs/developer-getting-started.md b/docs/developer-getting-started.md
new file mode 100644
index 0000000..45bbaf9
--- /dev/null
+++ b/docs/developer-getting-started.md
@@ -0,0 +1,118 @@
+
+# Developer Getting Started
+
+## Setting Up a Local Development Environment
+
+By following the steps for [externally running a controller](#running-the-controller-external-to-the-cluster) or 
+[running the controller inside a `KinD` cluster](#running-the-controller-inside-a-kind-cluster-with-ko), you can set up 
+a local environment to test your contributions before submitting a pull request.
+
+### Running the controller external to the cluster
+
+To test and run the project with your local changes, follow these steps to set up a development environment:
+
+1. Install Dependencies: Ensure you have the necessary dependencies installed, including:
+    - [Go](https://golang.org/doc/install) (version specified in `go.mod`).
+    - [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) for interacting with Kubernetes clusters.
+    - A local Kubernetes cluster such as [kind](https://kind.sigs.k8s.io/).
+
+2. Create a Local Kubernetes Cluster: If you don't already have a cluster, create one with your preferred tool. For example, with `kind`:
+    ```bash
+    kind create cluster
+    ```
+
+3. Install the Custom Resource Definitions (CRDs): Apply the latest CRDs to your cluster:
+    ```bash
+    make manifests
+    kubectl apply -k ./config/crd
+    ```
+
+4. Run the kro Controller Locally: Execute the controller with your changes:
+    ```bash
+    go run ./cmd/controller --log-level 2
+    ```
+    This will connect to the default Kubernetes context in your local kubeconfig (`~/.kube/config`). Ensure the context is pointing to your local cluster.
+
+### Running the controller inside a [`KinD`][kind] cluster with [`ko`][ko]
+
+[ko]: https://ko.build
+[kind]: https://kind.sigs.k8s.io/
+
+A helper Makefile target is used to (re)create a kind cluster, install the CRDs, build the container image and helm install the controller manifests in the kind cluster. 
+
+> _Note_: This target re-creates the kind cluster from scratch and should be used as a starting point or when you want to start over fresh.
+
+```sh
+KIND_CLUSTER_NAME=kro make deploy-kind
+```
+
+For iterating on an existing cluster, follow the instructions below.
+
+1. Prepare the cluster
+```sh
+export KIND_CLUSTER_NAME=my-other-cluster
+# Create a kind cluster if needed
+kind create cluster
+
+## Create the kro-system namespace
+kubectl create namespace kro-system || true
+
+```
+
+2. Build and install KRO components
+
+```sh
+## install the KRO CRDs
+make install
+
+# render the helm chart and apply using ko
+export KO_DOCKER_REPO=kind.local
+helm template kro ./helm \
+  --namespace kro-system \
+  --set image.pullPolicy=Never \
+  --set image.ko=true | ko apply -f -
+```
+
+### Dev Environment Hello World
+
+1. Create a `NoOp` ResourceGraph using the `ResourceGraphDefinition`.
+
+   ```sh
+   kubectl apply -f - <<EOF
+   apiVersion: kro.run/v1alpha1
+   kind: ResourceGraphDefinition
+   metadata:
+     name: noop
+   spec:
+     schema:
+       apiVersion: v1alpha1
+       kind: NoOp
+       spec: {}
+       status: {}
+     resources: []
+   EOF
+   ```
+
+   Inspect that the `ResourceGraphDefinition` was created, and also the newly created CRD `NoOp`.
+
+   ```sh
+   kubectl get ResourceGraphDefinition noop
+   kubectl get crds | grep noops
+   ```
+
+3. Create an instance of the new `NoOp` kind.
+
+   ```sh
+   kubectl apply -f - <<EOF
+   apiVersion: kro.run/v1alpha1
+   kind: NoOp
+   metadata:
+     name: demo
+   EOF
+   ```
+
+   And inspect the new instance,
+
+   ```shell
+   kubectl get noops -oyaml
+   ```
\ No newline at end of file
diff --git a/examples/apigateway/vpc-lattice/README.md b/examples/apigateway/vpc-lattice/README.md
new file mode 100644
index 0000000..32883e4
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/README.md
@@ -0,0 +1,170 @@
+# KRO VPC Lattice Demo
+
+This example demonstrates how to use KRO (Kubernetes Resource Orchestrator) to deploy and manage Amazon VPC Lattice resources on an Amazon EKS cluster, providing a streamlined approach to service networking in Kubernetes.
+
+## Overview
+
+Amazon VPC Lattice is a fully managed application networking service that enables you to connect, secure, and monitor all your services across multiple accounts and virtual private clouds (VPCs). This demo shows how to:
+
+1. Use KRO to define reusable resource graphs for VPC Lattice integration
+2. Implement Kubernetes Gateway API with the AWS Gateway Controller
+3. Deploy applications with simplified networking configuration
+4. Connect Kubernetes services to the VPC Lattice service network
+
+![Architecture Diagram](images/draw.png)
+
+
+## Prerequisites
+
+- Amazon EKS cluster
+- AWS CLI configured with appropriate permissions
+- kubectl installed and configured
+- Helm v3
+- jq
+
+## Installation
+
+### 1. Install KRO
+
+KRO (Kubernetes Resource Orchestrator) provides a way to define, deploy and manage complex Kubernetes resources as reusable templates.
+
+```bash
+export KRO_VERSION=$(curl -sL \
+    https://api.github.com/repos/kro-run/kro/releases/latest | \
+    jq -r '.tag_name | ltrimstr("v")'
+  )
+helm install kro oci://ghcr.io/kro-run/kro/kro \
+  --namespace kro \
+  --create-namespace \
+  --version=${KRO_VERSION}
+```
+
+### 2. Install Kubernetes Gateway API and AWS Gateway API Controller
+
+This step installs the Kubernetes Gateway API CRDs and the AWS Gateway API Controller for Amazon VPC Lattice integration.
+
+First, configure the necessary security group rules to allow VPC Lattice to communicate with your EKS cluster:
+
+```bash
+# Set environment variables
+export AWS_REGION=<your-aws-region>
+export EKS_CLUSTER_NAME=<your-eks-cluster-name>
+export LATTICE_IAM_ROLE=<your-lattice-iam-role-arn>
+
+# Configure security group rules
+CLUSTER_SG=$(aws eks describe-cluster --name $EKS_CLUSTER_NAME --output json| jq -r '.cluster.resourcesVpcConfig.clusterSecurityGroupId')
+PREFIX_LIST_ID=$(aws ec2 describe-managed-prefix-lists --query "PrefixLists[?PrefixListName=='com.amazonaws.$AWS_REGION.vpc-lattice'].PrefixListId" | jq -r '.[]')
+aws ec2 authorize-security-group-ingress --group-id $CLUSTER_SG --ip-permissions "PrefixListIds=[{PrefixListId=${PREFIX_LIST_ID}}],IpProtocol=-1"
+PREFIX_LIST_ID_IPV6=$(aws ec2 describe-managed-prefix-lists --query "PrefixLists[?PrefixListName=='com.amazonaws.$AWS_REGION.ipv6.vpc-lattice'].PrefixListId" | jq -r '.[]')
+aws ec2 authorize-security-group-ingress --group-id $CLUSTER_SG --ip-permissions "PrefixListIds=[{PrefixListId=${PREFIX_LIST_ID_IPV6}}],IpProtocol=-1"
+```
+
+Next, install the Kubernetes Gateway API and AWS Gateway Controller:
+
+```bash
+# Install Kubernetes Gateway API CRDs
+kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
+
+# Log in to AWS ECR public registry
+aws ecr-public get-login-password --region us-east-1 \
+  | helm registry login --username AWS --password-stdin public.ecr.aws
+
+# Get latest controller version
+LATTICE_CONTROLLER_VERSION=$(curl -sL \
+    https://api.github.com/repos/aws/aws-application-networking-k8s/releases/latest | \
+    jq -r '.tag_name | ltrimstr("v")'
+  )
+
+# Install AWS Gateway Controller
+helm install gateway-api-controller \
+    oci://public.ecr.aws/aws-application-networking-k8s/aws-gateway-controller-chart \
+    --version=v${LATTICE_CONTROLLER_VERSION} \
+    --create-namespace \
+    --set=aws.region=${AWS_REGION} \
+    --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"="$LATTICE_IAM_ROLE" \
+    --set=defaultServiceNetwork=kro-vpc-lattice-demo \
+    --namespace gateway-api-controller \
+    --wait
+```
+
+### Deploy gatewayClass and gateway resources 
+```
+kubectl apply -f resources/gatewayclass.yaml
+kubectl apply -f resources/gateway.yaml
+```
+
+
+## Resource Graph Definitions
+
+### 3. Deploy the Lattice Resource Graph Definition
+
+Create a Lattice Resource Graph Definition that maps VPC Lattice components to Kubernetes resources:
+
+```bash
+kubectl apply -f rg-lattice.yaml
+```
+
+The `rg-lattice.yaml` defines how to translate a simple schema into VPC Lattice-related Kubernetes Gateway API resources, including TargetGroupPolicy, and HTTPRoute.
+
+### 4. Deploy the WebApp Resource Graph Definition
+
+Create a WebApp Resource Graph Definition to simplify application deployment:
+
+```bash
+kubectl apply -f rg-webapp.yaml
+```
+
+The `rg-webapp.yaml` defines a template for deploying a web application with a Kubernetes Deployment and Service.
+
+### 5. Deploy the WebService Resource Graph Definition
+
+Create a WebService Resource Graph Definition that combines the WebApp and Lattice resources:
+
+```bash
+kubectl apply -f rg-webservice.yaml
+```
+
+The `rg-webservice.yaml` combines the WebApp and Lattice resource graphs, allowing you to deploy a web service with optional VPC Lattice integration.
+
+## Deploying Your Application
+
+### 6. Deploy Your Application Instance
+
+Create an instance of the WebService with VPC Lattice integration enabled:
+
+```bash
+kubectl apply -f instance.yaml
+```
+
+## Architecture
+
+This project implements a three-layer architecture:
+
+1. **Base Resources Layer**: Core Kubernetes resources (TargetGroupPolicy, HTTPRoute, Deployment, Service)
+2. **Resource Graph Layer**: Reusable templates (Lattice, WebApp, WebService) that abstract the base resources
+3. **Instance Layer**: Application-specific instances created from the resource graphs
+
+## Accessing Your Service
+
+Once deployed, your service will be accessible through the VPC Lattice service network. You can find the VPC Lattice endpoint using the AWS Console or the following AWS CLI command:
+
+```bash
+aws vpc-lattice list-service-networks
+aws vpc-lattice list-services --service-network-identifier kro-vpc-lattice-demo
+```
+
+## Benefits of This Approach
+
+- **Simplified Configuration**: Reduce complexity by abstracting away the detailed configuration of VPC Lattice resources
+- **Reusable Templates**: Create standardized deployment patterns that can be reused across multiple applications
+- **Declarative Approach**: Define the desired state of your infrastructure and application components
+- **Improved Developer Experience**: Allow developers to deploy applications with VPC Lattice integration without deep networking knowledge
+
+## Troubleshooting
+
+- Ensure your EKS cluster has the required IAM permissions for VPC Lattice integration
+- Verify the security group rules are correctly configured to allow VPC Lattice traffic
+- Check the Gateway API Controller logs for any errors:
+  ```bash
+  kubectl logs -n gateway-api-controller -l app.kubernetes.io/name=gateway-api-controller
+  ```
diff --git a/examples/apigateway/vpc-lattice/images/draw.png b/examples/apigateway/vpc-lattice/images/draw.png
new file mode 100644
index 0000000..de908e4
Binary files /dev/null and b/examples/apigateway/vpc-lattice/images/draw.png differ
diff --git a/examples/apigateway/vpc-lattice/instance.yaml b/examples/apigateway/vpc-lattice/instance.yaml
new file mode 100644
index 0000000..e0d3dfe
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/instance.yaml
@@ -0,0 +1,10 @@
+apiVersion: kro.run/v1alpha1
+kind: WebService
+metadata:
+  name: test-app
+spec:
+  name: test-app
+  image: nginx
+  service: {}
+  vpclattice:
+    enabled: true
diff --git a/examples/apigateway/vpc-lattice/resources/gateway.yaml b/examples/apigateway/vpc-lattice/resources/gateway.yaml
new file mode 100644
index 0000000..e8f85b8
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/resources/gateway.yaml
@@ -0,0 +1,14 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: kro-vpc-lattice-demo
+  namespace: default
+spec:
+  gatewayClassName: amazon-vpc-lattice
+  listeners:
+  - allowedRoutes:
+      namespaces:
+        from: Same
+    name: http
+    port: 80
+    protocol: HTTP
\ No newline at end of file
diff --git a/examples/apigateway/vpc-lattice/resources/gatewayclass.yaml b/examples/apigateway/vpc-lattice/resources/gatewayclass.yaml
new file mode 100644
index 0000000..472474e
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/resources/gatewayclass.yaml
@@ -0,0 +1,6 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: amazon-vpc-lattice
+spec:
+  controllerName: application-networking.k8s.aws/gateway-api-controller
diff --git a/examples/apigateway/vpc-lattice/rg-lattice.yaml b/examples/apigateway/vpc-lattice/rg-lattice.yaml
new file mode 100644
index 0000000..26cb63e
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/rg-lattice.yaml
@@ -0,0 +1,60 @@
+apiVersion: kro.run/v1alpha1
+kind: ResourceGraphDefinition
+metadata:
+  name: lattice.kro.run
+spec:
+  schema:
+    apiVersion: v1alpha1
+    kind: Lattice
+    spec:
+      name: string
+      namespace: string | default=default
+      port: integer | default=80
+      vpcLatticeName: string | default="kro-vpc-lattice-demo"
+      listenerProtocol: string | default="HTTP"
+
+  resources:
+  - id: targetgrouppolicy
+    template:
+      apiVersion: application-networking.k8s.aws/v1alpha1
+      kind: TargetGroupPolicy
+      metadata:
+        name: ${schema.spec.vpcLatticeName}
+        namespace: ${schema.spec.namespace}
+      spec:
+        targetRef:
+          group: ""
+          kind: Service
+          name: ${schema.spec.name}
+        protocol: ${schema.spec.listenerProtocol}
+        protocolVersion: HTTP1
+        healthCheck:
+          enabled: true
+          intervalSeconds: 10
+          timeoutSeconds: 1
+          healthyThresholdCount: 3
+          unhealthyThresholdCount: 2
+          path: "/"
+          port: ${schema.spec.port}
+          protocol: HTTP
+          protocolVersion: HTTP1
+          statusMatch: "200"
+
+  - id: httproute
+    template:
+      apiVersion: gateway.networking.k8s.io/v1beta1
+      kind: HTTPRoute
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+      spec:
+        parentRefs:
+          - name: ${schema.spec.vpcLatticeName}
+            sectionName: http
+        rules:
+          - backendRefs:
+              - name: ${schema.spec.name}
+                namespace: ${schema.spec.namespace}
+                kind: Service
+                port: ${schema.spec.port}
+                weight: 100
diff --git a/examples/apigateway/vpc-lattice/rg-webapp.yaml b/examples/apigateway/vpc-lattice/rg-webapp.yaml
new file mode 100644
index 0000000..b741f0a
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/rg-webapp.yaml
@@ -0,0 +1,78 @@
+apiVersion: kro.run/v1alpha1
+kind: ResourceGraphDefinition
+metadata:
+  name: webapp.kro.run
+spec:
+  schema:
+    apiVersion: v1alpha1
+    kind: WebApp
+    spec:
+      name: string
+      namespace: string | default=default
+      image: string | default=nginx
+      port: integer | default=80
+      replicas: integer | default=3
+      service:
+        enabled: boolean | default=true
+      serviceAccount: string | default=default
+    status:
+      deploymentConditions: ${deployment.status.conditions}
+      availableReplicas: ${deployment.status.availableReplicas}
+
+  resources:
+  - id: deployment
+    readyWhen:
+      - ${deployment.spec.replicas == deployment.status.availableReplicas}
+    template:
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}
+      spec:
+        replicas: ${schema.spec.replicas}
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: ${schema.spec.name}
+            app: ${schema.spec.name}
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: ${schema.spec.name}
+              app: ${schema.spec.name}
+          spec:
+            serviceAccountName: ${schema.spec.serviceAccount}
+            containers:
+            - name: ${schema.spec.name}
+              image: ${schema.spec.image}
+              imagePullPolicy: Always
+              ports:
+              - containerPort: ${schema.spec.port}
+              resources:
+                requests:
+                  memory: "64Mi"
+                  cpu: "250m"
+                limits:
+                  memory: "1Gi"
+                  cpu: "1"
+            restartPolicy: Always
+
+  - id: service
+    includeWhen:
+    - ${schema.spec.service.enabled}  
+    template:
+      apiVersion: v1
+      kind: Service
+      metadata:
+        name: ${deployment.metadata.name}
+        namespace: ${deployment.metadata.namespace}
+      spec:
+        selector:
+          app: ${schema.spec.name}
+        ports:
+        - name: http
+          protocol: TCP
+          port: ${schema.spec.port}
+          targetPort: ${schema.spec.port}
diff --git a/examples/apigateway/vpc-lattice/rg-webservice.yaml b/examples/apigateway/vpc-lattice/rg-webservice.yaml
new file mode 100644
index 0000000..ccf1355
--- /dev/null
+++ b/examples/apigateway/vpc-lattice/rg-webservice.yaml
@@ -0,0 +1,59 @@
+apiVersion: kro.run/v1alpha1
+kind: ResourceGraphDefinition
+metadata:
+  name: webservice.kro.run
+spec:
+  schema:
+    apiVersion: v1alpha1
+    kind: WebService
+    spec:
+      name: string
+      namespace: string | default=default
+      image: string | default=nginx
+      port: integer | default=80
+      replicas: integer | default=3
+      service:
+        enabled: boolean | default=true
+      serviceAccount: string | default=default
+      vpclattice:
+        enabled: boolean | default=false
+      vpcLatticeName: string | default="kro-vpc-lattice-demo"
+      gatewayClassName: string | default=amazon-vpc-lattice
+      listenerProtocol: string | default="HTTP"
+
+
+  resources:
+  - id: webapp
+    template:
+      apiVersion: kro.run/v1alpha1
+      kind: WebApp
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}
+      spec:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        image: ${schema.spec.image}
+        port: ${schema.spec.port}
+        replicas: ${schema.spec.replicas}
+        service:
+          enabled: ${schema.spec.service.enabled}
+        serviceAccount: ${schema.spec.serviceAccount}
+
+  - id: lattice
+    includeWhen:
+    - ${schema.spec.vpclattice.enabled}
+    template:
+      apiVersion: kro.run/v1alpha1
+      kind: Lattice
+      metadata:
+        name: ${schema.spec.name}
+      spec:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        port: ${schema.spec.port}
+        vpcLatticeName: ${schema.spec.vpcLatticeName}
+        listenerProtocol: ${schema.spec.listenerProtocol}
+        gatewayClassName: ${schema.spec.gatewayClassName}
diff --git a/examples/aws/llm/README.md b/examples/aws/llm/README.md
new file mode 100644
index 0000000..ecc4be7
--- /dev/null
+++ b/examples/aws/llm/README.md
@@ -0,0 +1,116 @@
+# Ollama Kubernetes Deployment
+
+Resource Graph Definition (RGD) for deploying Ollama with various LLM models. It supports both CPU and GPU deployments with configurable resources and automatic model pulling.
+
+## Overview
+
+The RGD creates the following Kubernetes resources:
+- Storage Class (AWS EBS GP3)
+- Persistent Volume Claim for model storage
+- Model Pull Deployment (prepares the model)
+- Serve Deployment (runs the inference service)
+- Service (internal networking)
+- Ingress (optional, for external access)
+
+## Prerequisites
+
+### Required
+- Kubernetes cluster
+- `kubectl` configured to access your cluster
+- AWS EBS CSI Driver
+  ```bash
+  helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver
+  helm install aws-ebs-csi-driver aws-ebs-csi-driver/aws-ebs-csi-driver
+  ```
+
+### Optional (Based on Configuration)
+- AWS Load Balancer Controller (required if ingress is enabled)
+  ```bash
+  helm repo add eks https://aws.github.io/eks-charts
+  helm install aws-load-balancer-controller eks/aws-load-balancer-controller
+  ```
+- NVIDIA GPU Operators (required if GPU is enabled)
+  ```bash
+  helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
+  helm install nvidia-device-plugin nvidia/gpu-operator
+  ```
+
+## Configuration
+
+Create an instance YAML file with your desired configuration:
+
+```yaml
+apiVersion: kro.run/v1alpha1
+kind: OllamaDeployment
+metadata:
+  name: llm
+spec:
+  name: llm        
+  namespace: default
+  values:
+    storage: 50Gi        # Storage for model files
+    model:
+      name: llama3.2 # phi | deepseek - Model name
+      size: 1b         # Model size variant
+    resources:
+      requests:
+        cpu: "2"
+        memory: "8Gi"
+      limits:
+        cpu: "4"
+        memory: "16Gi"
+    gpu:
+      enabled: false     # Set to true for GPU acceleration
+      count: 1          # deployment limit for number of GPUs to allocate
+    ingress:
+      enabled: false    # Set to true for external access
+      port: 80         # Ingress port
+```
+
+## Deployment
+
+1. Apply the RGD:
+```bash
+kubectl apply -f RGD.yaml
+```
+
+2. Create your instance:
+```bash
+kubectl apply -f instance.yaml
+```
+
+## Usage
+
+### Internal Access
+Access the model within the cluster using:
+```bash
+curl http://<name>.<namespace>.svc.cluster.local/v1/completions \
+  -H 'Content-Type: application/json' \
+  -d '{ 
+    "model": "llama3.2:1b",
+    "prompt": "Hello, who are you?"
+  }'
+```
+
+### External Access (if ingress enabled)
+The service will be available at:
+```bash
+curl http://<ALB-ADDRESS>/<name>/v1/completions \
+  -H 'Content-Type: application/json' \
+  -d '{ 
+    "model": "llama3.2:1b",
+    "prompt": "Hello, who are you?"
+  }'
+```
+
+## Resource Requirements
+
+### Minimum Requirements
+- CPU deployment: 2 CPU cores, 8GB RAM
+- GPU deployment: 2 CPU cores, 8GB RAM, 1 NVIDIA GPU
+- Storage: 50GB (adjust based on model size)
+
+### Scaling Considerations
+- Increase storage for larger models
+- Adjust CPU/Memory based on load
+- Add GPUs for better inference performance
diff --git a/examples/aws/llm/instance.yaml b/examples/aws/llm/instance.yaml
new file mode 100644
index 0000000..66a5184
--- /dev/null
+++ b/examples/aws/llm/instance.yaml
@@ -0,0 +1,25 @@
+apiVersion: kro.run/v1alpha1
+kind: OllamaDeployment
+metadata:
+  name: llm
+spec:
+  name: llm
+  namespace: default
+  values:
+    storage: 50Gi 
+    model:
+      name: llama3.2 # phi | deepseek
+      size: 1b
+    resources:
+      requests:
+        cpu: "2"
+        memory: "8Gi"
+      limits:
+        cpu: "4"
+        memory: "16Gi"
+    gpu:
+      enabled: false
+      count: 1
+    ingress:
+      enabled: false
+      port: 80
diff --git a/examples/aws/llm/llm.yaml b/examples/aws/llm/llm.yaml
new file mode 100644
index 0000000..0b5a2d4
--- /dev/null
+++ b/examples/aws/llm/llm.yaml
@@ -0,0 +1,302 @@
+apiVersion: kro.run/v1alpha1
+kind: ResourceGraphDefinition
+metadata:
+  name: ollama-deployment.kro.run
+  namespace: default
+spec:
+  schema:
+    apiVersion: v1alpha1
+    kind: OllamaDeployment
+    spec:
+      name: string | default=ollama
+      namespace: string | default=default
+      values:
+        # Storage configuration for model files
+        storage: string | default=50Gi # Minimum storage needed for model files
+        
+        # Model configuration
+        model:
+          name: string | default=llama3.2 # phi | deepseek - Name of the LLM model to deploy
+          size: string | default=1b         # Size variant of the model
+        
+        # Resource allocation for the Ollama containers
+        resources:
+          requests:
+            cpu: string | default="2"
+            memory: string | default="8Gi"
+          limits:
+            cpu: string | default="4"
+            memory: string | default="16Gi"
+        
+        # GPU configuration for hardware acceleration
+        gpu:
+          enabled: boolean | default=false  # Set to true to use GPU acceleration
+          count: integer | default=1        # Number of GPUs to allocate
+        
+        # Ingress configuration for external access
+        ingress:
+          enabled: boolean | default=true   # Enable/disable external access
+          port: integer | default=80        # Port for ingress traffic
+
+  resources:
+  - id: ollamaStorageClass
+    template:
+      apiVersion: storage.k8s.io/v1
+      kind: StorageClass
+      metadata:
+        name: ${schema.spec.name}-storage-class
+        annotations:
+          storageclass.kubernetes.io/is-default-class: "true"
+      parameters:
+        type: gp3
+      provisioner: ebs.csi.aws.com
+      reclaimPolicy: Delete
+      volumeBindingMode: WaitForFirstConsumer
+      allowVolumeExpansion: true
+
+  - id: ollamaPersistentVolumeClaim
+    template:
+      apiVersion: v1
+      kind: PersistentVolumeClaim
+      metadata:
+        annotations:
+          volume.kubernetes.io/storage-provisioner: ebs.csi.aws.com
+        name: ${schema.spec.name}-pvc
+        namespace: ${schema.spec.namespace}
+      spec:
+        accessModes:
+        - ReadWriteOnce
+        resources:
+          requests:
+            storage: ${schema.spec.values.storage}
+        storageClassName: ${schema.spec.name}-storage-class
+        volumeMode: Filesystem
+
+  - id: ollamaModelPullDeployment
+    includeWhen:
+    - ${schema.spec.values.gpu.enabled == false}
+    template:
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: ${schema.spec.name}-pull
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}-pull
+      spec:
+        replicas: 1
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: ${schema.spec.name}-pull
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: ${schema.spec.name}-pull
+          spec:
+            volumes:
+            - name: ${schema.spec.name}-volume
+              persistentVolumeClaim:
+                claimName: ${schema.spec.name}-pvc  # <-- same PVC that the 'serve' deployment will use
+            containers:
+            - name: pull-model
+              image: ollama/ollama:latest
+              imagePullPolicy: IfNotPresent
+              env:
+              - name: OLLAMA_KEEP_ALIVE
+                value: "-1"
+              # Pull the model, then keep the container alive
+              lifecycle:
+                postStart:
+                  exec:
+                    command: 
+                    - "/bin/sh"
+                    - "-c"
+                    - "ollama run ${schema.spec.values.model.name}:${schema.spec.values.model.size}"
+              ports:
+              - containerPort: 11434
+              volumeMounts:
+              - name: ${schema.spec.name}-volume
+                mountPath: /root/.ollama
+
+  - id: ollamaServeDeployment
+    includeWhen:
+    - ${schema.spec.values.gpu.enabled == false}
+    template:
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}
+      spec:
+        replicas: 1
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: ${schema.spec.name}
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: ${schema.spec.name}
+          spec:
+            volumes:
+            - name: ${schema.spec.name}-volume
+              persistentVolumeClaim:
+                claimName: ${schema.spec.name}-pvc  # <-- must match the first deployment
+            containers:
+            - name: serve
+              image: ollama/ollama:latest
+              imagePullPolicy: IfNotPresent
+              env:
+              - name: OLLAMA_KEEP_ALIVE
+                value: "-1"
+              command: ["ollama", "serve"] # Serve model
+              ports:
+              - containerPort: 11434
+              resources:
+                limits:
+                  cpu: ${schema.spec.values.resources.limits.cpu}
+                  memory: ${schema.spec.values.resources.limits.memory}
+                requests:
+                  cpu: ${schema.spec.values.resources.requests.cpu}
+                  memory: ${schema.spec.values.resources.requests.memory}
+              volumeMounts:
+              - name: ${schema.spec.name}-volume
+                mountPath: /root/.ollama
+
+  - id: ollamaModelPullDeploymentGPU
+    includeWhen:
+    - ${schema.spec.values.gpu.enabled}
+    template:
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: ${schema.spec.name}-pull
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}-pull
+      spec:
+        replicas: 1
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: ${schema.spec.name}-pull
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: ${schema.spec.name}-pull
+          spec:
+            volumes:
+            - name: ${schema.spec.name}-volume
+              persistentVolumeClaim:
+                claimName: ${schema.spec.name}-pvc  # <-- same PVC that the 'serve' deployment will use
+            containers:
+            - name: pull-model
+              image: ollama/ollama:latest
+              imagePullPolicy: IfNotPresent
+              env:
+              - name: OLLAMA_KEEP_ALIVE
+                value: "-1"
+              # Pull the model, then keep the container alive
+              lifecycle:
+                postStart:
+                  exec:
+                    command: 
+                    - "/bin/sh"
+                    - "-c"
+                    - "ollama run ${schema.spec.values.model.name}:${schema.spec.values.model.size}"
+              ports:
+              - containerPort: 11434
+              volumeMounts:
+              - name: ${schema.spec.name}-volume
+                mountPath: /root/.ollama
+
+  - id: ollamaServeDeploymentGPU
+    includeWhen:
+    - ${schema.spec.values.gpu.enabled}
+    template:
+      apiVersion: apps/v1
+      kind: Deployment
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+        labels:
+          app.kubernetes.io/name: ${schema.spec.name}
+      spec:
+        replicas: 1
+        selector:
+          matchLabels:
+            app.kubernetes.io/name: ${schema.spec.name}
+        template:
+          metadata:
+            labels:
+              app.kubernetes.io/name: ${schema.spec.name}
+          spec:
+            nodeSelector:        
+              nvidia.com/gpu: ${schema.spec.values.gpu.enabled} # <-- label for gpu
+            volumes:
+            - name: ${schema.spec.name}-volume
+              persistentVolumeClaim:
+                claimName: ${schema.spec.name}-pvc  # <-- must match the first deployment
+            containers:
+            - name: serve
+              image: ollama/ollama:latest
+              imagePullPolicy: IfNotPresent
+              env:
+              - name: OLLAMA_KEEP_ALIVE
+                value: "-1"
+              command: ["ollama", "serve"] # Serve model
+              ports:
+              - containerPort: 11434
+              resources:
+                limits:
+                  nvidia.com/gpu: ${schema.spec.values.gpu.count}
+                  cpu: ${schema.spec.values.resources.limits.cpu}
+                  memory: ${schema.spec.values.resources.limits.memory}
+                requests:
+                  cpu: ${schema.spec.values.resources.requests.cpu}
+                  memory: ${schema.spec.values.resources.requests.memory}
+              volumeMounts:
+              - name: ${schema.spec.name}-volume
+                mountPath: /root/.ollama
+
+  - id: ollamaService
+    template:
+      apiVersion: v1
+      kind: Service
+      metadata:
+        name: ${schema.spec.name}
+        namespace: ${schema.spec.namespace}
+      spec:
+        selector:
+          app.kubernetes.io/name: ${schema.spec.name}
+        ports:
+          - protocol: TCP
+            port: 80
+            targetPort: 11434
+  
+  - id: ollamaIngress
+    includeWhen:
+    - ${schema.spec.values.ingress.enabled} 
+    template:
+      apiVersion: networking.k8s.io/v1
+      kind: Ingress
+      metadata:
+        name: ${schema.spec.name}-ingress
+        namespace: ${schema.spec.namespace}
+        annotations:
+          alb.ingress.kubernetes.io/scheme: internet-facing
+          alb.ingress.kubernetes.io/target-type: ip
+          alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
+          alb.ingress.kubernetes.io/backend-protocol: HTTP
+      spec:
+        rules:
+          - http:
+              paths:
+                - path: /${schema.spec.name}
+                  pathType: Prefix
+                  backend:
+                    service:
+                      name: ${schema.spec.name}
+                      port:
+                        number: ${schema.spec.values.ingress.port}
+
diff --git a/examples/aws/webstack/Readme.md b/examples/aws/webstack/Readme.md
index c8b9f84..6e6b302 100644
--- a/examples/aws/webstack/Readme.md
+++ b/examples/aws/webstack/Readme.md
@@ -3,8 +3,8 @@
 This example creates a ResourceGraphDefinition called `WebStack` comprised of
 three other RGs: `WebApp`, `S3Bucket`, and `PodIdentity`
 
-![Netsted RGD Instance](../../images/architecture-diagrams/kro-WebStack.png)
-_Fugure 1: Nested RGD Example_
+![Netsted RGD Instance](../../../images/architecture-diagrams/kro-WebStack.png)
+_Figure 1: Nested RGD Example_
 
 ### Create ResourceGraphDefinitions
 
@@ -17,8 +17,8 @@ cd examples/
 Apply the RGs to your cluster:
 
 ```
-kubectl apply -f podidenity/rg.yaml -f s3bucket/rg.yaml -f webapp/rg.yaml
-kubectl apply -f webstack/rg.yaml
+kubectl apply -f aws/podidenity/rg.yaml -f aws/s3bucket/rg.yaml -f kubernetes/webapp/rg.yaml
+kubectl apply -f aws/webstack/rg.yaml
 ```
 
 Validate the RGs statuses are Active:
@@ -63,6 +63,23 @@ a new file called instance.yaml.
 ```shell
 envsubst < "webstack/instance-tmpl.yaml" > "webstack/instance.yaml"
 ```
+`clusterName` is a required field to achieve pod identity association. If your cluster name is not 'kro' then replace the value assigned to this field in the `webstack/instance.yaml` file with the name of your cluter. 
+
+<pre>
+apiVersion: kro.run/v1alpha1
+kind: WebStack
+metadata:
+  name: test-app
+spec:
+  name: my-test-app-name
+  <mark>clusterName:</mark> kro # change this value if your cluster name is not kro
+  image: candonov/s3-demo-app
+  s3bucket:
+    enabled: true
+    access: write
+  ingress:
+    enabled: true # this will expose unathenticated alb
+  service: {}`</pre>
 
 Apply the `webstack/instance.yaml`
 
diff --git a/examples/aws/webstack/instance-tmpl.yaml b/examples/aws/webstack/instance-tmpl.yaml
index b29bd13..e7c5195 100644
--- a/examples/aws/webstack/instance-tmpl.yaml
+++ b/examples/aws/webstack/instance-tmpl.yaml
@@ -4,6 +4,7 @@ metadata:
   name: test-app
 spec:
   name: $RESOURCES_NAME
+  clusterName: kro # change this value if your cluster name is not kro
   image: candonov/s3-demo-app
   s3bucket:
     enabled: true
diff --git a/go.mod b/go.mod
index b45ab8d..a6aba9e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,6 @@
 module github.com/kro-run/kro
 
-go 1.22.0
-
-toolchain go1.22.4
+go 1.24.0
 
 require (
 	github.com/go-logr/logr v1.4.2
diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml
index 642e9fa..e7f8464 100644
--- a/helm/templates/deployment.yaml
+++ b/helm/templates/deployment.yaml
@@ -57,6 +57,18 @@ spec:
               value: {{ .Values.config.dynamicControllerConcurrentReconciles | quote }}
             - name: KRO_LOG_LEVEL
               value: {{ .Values.config.logLevel | quote }}
+            - name: KRO_DYNAMIC_CONTROLLER_DEFAULT_RESYNC_PERIOD
+              value: {{ .Values.config.dynamicControllerDefaultResyncPeriod | quote }}
+            - name: KRO_DYNAMIC_CONTROLLER_DEFAULT_QUEUE_MAX_RETRIES
+              value: {{ .Values.config.dynamicControllerDefaultQueueMaxRetries | quote }}
+            - name: KRO_DYNAMIC_CONTROLLER_DEFAULT_SHUTDOWN_TIMEOUT
+              value: {{ .Values.config.dynamicControllerDefaultShutdownTimeout | quote }}
+            - name: KRO_CLIENT_QPS
+              value: {{ .Values.config.clientQps | quote }}
+            - name: KRO_CLIENT_BURST
+              value: {{ .Values.config.clientBurst | quote }}
+            - name: KRO_LEADER_ELECTION
+              value: {{ .Values.config.enableLeaderElection | quote }}
           args:
             - --allow-crd-deletion
             - "$(KRO_ALLOW_CRD_DELETION)"
@@ -70,6 +82,18 @@ spec:
             - "$(KRO_DYNAMIC_CONTROLLER_CONCURRENT_RECONCILES)"
             - --log-level
             - "$(KRO_LOG_LEVEL)"
+            - --dynamic-controller-default-resync-period
+            - "$(KRO_DYNAMIC_CONTROLLER_DEFAULT_RESYNC_PERIOD)"
+            - --dynamic-controller-default-queue-max-retries
+            - "$(KRO_DYNAMIC_CONTROLLER_DEFAULT_QUEUE_MAX_RETRIES)"
+            - --dynamic-controller-default-shutdown-timeout
+            - "$(KRO_DYNAMIC_CONTROLLER_DEFAULT_SHUTDOWN_TIMEOUT)"
+            - --client-qps
+            - "$(KRO_CLIENT_QPS)"
+            - --client-burst
+            - "$(KRO_CLIENT_BURST)"
+            - --leader-elect
+            - "$(KRO_LEADER_ELECTION)"
           livenessProbe:
             httpGet:
               path: /healthz
diff --git a/helm/values.yaml b/helm/values.yaml
index 827ab45..dca9f22 100644
--- a/helm/values.yaml
+++ b/helm/values.yaml
@@ -35,7 +35,7 @@ deployment:
   # Number of replicas for the Pods to run
   replicaCount: 1
   # Port that the container will expose
-  containerPort: 8080
+  containerPort: 8078
   # Annotations to add to the Pod
   podAnnotations: {}
   # Labels to add to the Pod
@@ -84,6 +84,12 @@ deployment:
 config:
   # Allow kro to delete CRDs
   allowCRDDeletion: false
+  # The maximum number of queries per second to allow
+  clientQps: 100
+  # The number of requests that can be stored for processing before the server starts enforcing the QPS limit
+  clientBurst: 150
+  # Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
+  enableLeaderElection: false
   # The address the metric endpoint binds to
   metricsBindAddress: :8078
   # The address the probe endpoint binds to
@@ -92,6 +98,12 @@ config:
   resourceGraphDefinitionConcurrentReconciles: 1
   # The number of dynamic controller reconciles to run in parallel
   dynamicControllerConcurrentReconciles: 1
+  # The interval at which the controller will re list resources even with no changes, in hours
+  dynamicControllerDefaultResyncPeriod: 10
+  # The maximum number of retries for an item in the queue will be retried before being dropped
+  dynamicControllerDefaultQueueMaxRetries: 20
+  # The maximum duration to wait for the controller to gracefully shutdown, in seconds
+  dynamicControllerDefaultShutdownTimeout: 60
   # The log level verbosity. 0 is the least verbose, 5 is the most verbose
   logLevel: 3
 
diff --git a/pkg/controller/resourcegraphdefinition/controller_reconcile.go b/pkg/controller/resourcegraphdefinition/controller_reconcile.go
index dd8c7d8..b4a1190 100644
--- a/pkg/controller/resourcegraphdefinition/controller_reconcile.go
+++ b/pkg/controller/resourcegraphdefinition/controller_reconcile.go
@@ -43,18 +43,21 @@ func (r *ResourceGraphDefinitionReconciler) reconcileResourceGraphDefinition(ctx
 		return nil, nil, err
 	}
 
-	// Ensure CRD exists and is up to date
-	log.V(1).Info("reconciling resource graph definition CRD")
-	if err := r.reconcileResourceGraphDefinitionCRD(ctx, processedRGD.Instance.GetCRD()); err != nil {
-		return processedRGD.TopologicalOrder, resourcesInfo, err
-	}
-
 	// Setup metadata labeling
 	graphExecLabeler, err := r.setupLabeler(rgd)
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to setup labeler: %w", err)
 	}
 
+	crd := processedRGD.Instance.GetCRD()
+	graphExecLabeler.ApplyLabels(&crd.ObjectMeta)
+
+	// Ensure CRD exists and is up to date
+	log.V(1).Info("reconciling resource graph definition CRD")
+	if err := r.reconcileResourceGraphDefinitionCRD(ctx, crd); err != nil {
+		return processedRGD.TopologicalOrder, resourcesInfo, err
+	}
+
 	// Setup and start microcontroller
 	gvr := processedRGD.Instance.GetGroupVersionResource()
 	controller := r.setupMicroController(gvr, processedRGD, rgd.Spec.DefaultServiceAccounts, graphExecLabeler)
diff --git a/pkg/dynamiccontroller/dynamic_controller.go b/pkg/dynamiccontroller/dynamic_controller.go
index ec6fa82..24439b0 100644
--- a/pkg/dynamiccontroller/dynamic_controller.go
+++ b/pkg/dynamiccontroller/dynamic_controller.go
@@ -123,7 +123,7 @@ type DynamicController struct {
 	handlers sync.Map
 
 	// queue is the workqueue used to process items
-	queue workqueue.RateLimitingInterface
+	queue workqueue.TypedRateLimitingInterface[ObjectIdentifiers]
 
 	log logr.Logger
 }
@@ -147,10 +147,10 @@ func NewDynamicController(
 		config:     config,
 		kubeClient: kubeClient,
 		// TODO(a-hilaly): Make the queue size configurable.
-		queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewMaxOfRateLimiter(
-			workqueue.NewItemExponentialFailureRateLimiter(200*time.Millisecond, 1000*time.Second),
-			&workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
-		), "dynamic-controller-queue"),
+		queue: workqueue.NewTypedRateLimitingQueueWithConfig(workqueue.NewTypedMaxOfRateLimiter(
+			workqueue.NewTypedItemExponentialFailureRateLimiter[ObjectIdentifiers](200*time.Millisecond, 1000*time.Second),
+			&workqueue.TypedBucketRateLimiter[ObjectIdentifiers]{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
+		), workqueue.TypedRateLimitingQueueConfig[ObjectIdentifiers]{Name: "dynamic-controller-queue"}),
 		log: logger,
 		// pass version and pod id from env
 	}
@@ -232,24 +232,17 @@ func (dc *DynamicController) worker(ctx context.Context) {
 
 // processNextWorkItem processes a single item from the queue.
 func (dc *DynamicController) processNextWorkItem(ctx context.Context) bool {
-	obj, shutdown := dc.queue.Get()
+	item, shutdown := dc.queue.Get()
 	if shutdown {
 		return false
 	}
-	defer dc.queue.Done(obj)
+	defer dc.queue.Done(item)
 
 	queueLength.Set(float64(dc.queue.Len()))
 
-	item, ok := obj.(ObjectIdentifiers)
-	if !ok {
-		dc.log.Error(fmt.Errorf("expected ObjectIdentifiers in queue but got %#v", obj), "Invalid item in queue")
-		dc.queue.Forget(obj)
-		return true
-	}
-
 	err := dc.syncFunc(ctx, item)
 	if err == nil || apierrors.IsNotFound(err) {
-		dc.queue.Forget(obj)
+		dc.queue.Forget(item)
 		return true
 	}
 
@@ -260,25 +253,25 @@ func (dc *DynamicController) processNextWorkItem(ctx context.Context) bool {
 	case *requeue.NoRequeue:
 		dc.log.Error(typedErr, "Error syncing item, not requeuing", "item", item)
 		requeueTotal.WithLabelValues(gvrKey, "no_requeue").Inc()
-		dc.queue.Forget(obj)
+		dc.queue.Forget(item)
 	case *requeue.RequeueNeeded:
 		dc.log.V(1).Info("Requeue needed", "item", item, "error", typedErr)
 		requeueTotal.WithLabelValues(gvrKey, "requeue").Inc()
-		dc.queue.Add(obj) // Add without rate limiting
+		dc.queue.Add(item) // Add without rate limiting
 	case *requeue.RequeueNeededAfter:
 		dc.log.V(1).Info("Requeue needed after delay", "item", item, "error", typedErr, "delay", typedErr.Duration())
 		requeueTotal.WithLabelValues(gvrKey, "requeue_after").Inc()
-		dc.queue.AddAfter(obj, typedErr.Duration())
+		dc.queue.AddAfter(item, typedErr.Duration())
 	default:
 		// Arriving here means we have an unexpected error, we should requeue the item
 		// with rate limiting.
 		requeueTotal.WithLabelValues(gvrKey, "rate_limited").Inc()
-		if dc.queue.NumRequeues(obj) < dc.config.QueueMaxRetries {
+		if dc.queue.NumRequeues(item) < dc.config.QueueMaxRetries {
 			dc.log.Error(err, "Error syncing item, requeuing with rate limit", "item", item)
-			dc.queue.AddRateLimited(obj)
+			dc.queue.AddRateLimited(item)
 		} else {
 			dc.log.Error(err, "Dropping item from queue after max retries", "item", item)
-			dc.queue.Forget(obj)
+			dc.queue.Forget(item)
 		}
 	}
 
diff --git a/pkg/graph/emulator/emulator.go b/pkg/graph/emulator/emulator.go
index 62eaafb..4321172 100644
--- a/pkg/graph/emulator/emulator.go
+++ b/pkg/graph/emulator/emulator.go
@@ -44,7 +44,8 @@ func NewEmulator() *Emulator {
 }
 
 // GenerateDummyCR generates a dummy CR based on the provided schema.
-func (e *Emulator) GenerateDummyCR(gvk schema.GroupVersionKind, schema *spec.Schema) (*unstructured.Unstructured, error) {
+func (e *Emulator) GenerateDummyCR(gvk schema.GroupVersionKind,
+	schema *spec.Schema) (*unstructured.Unstructured, error) {
 	if schema == nil {
 		return nil, fmt.Errorf("schema is nil for %v", gvk)
 	}
@@ -70,6 +71,7 @@ func (e *Emulator) GenerateDummyCR(gvk schema.GroupVersionKind, schema *spec.Sch
 	cr.SetKind(gvk.Kind)
 	cr.SetName(fmt.Sprintf("%s-sample", strings.ToLower(gvk.Kind)))
 	cr.SetNamespace("default")
+	cr.SetResourceVersion(fmt.Sprintf("%d", e.rand.Intn(1000)))
 	return cr, nil
 }
 
@@ -79,6 +81,11 @@ func (e *Emulator) generateValue(schema *spec.Schema) (interface{}, error) {
 		return nil, fmt.Errorf("schema is nil")
 	}
 
+	if enabled, ok := schema.VendorExtensible.Extensions["x-kubernetes-preserve-unknown-fields"]; ok && enabled.(bool) {
+		// Handle x-kubernetes-preserve-unknown-fields
+		return e.generateObject(schema)
+	}
+
 	if enabled, ok := schema.VendorExtensible.Extensions["x-kubernetes-int-or-string"]; ok && enabled.(bool) {
 		// Default to integer for dummy CRs
 		return e.generateInteger(schema), nil
diff --git a/pkg/graph/parser/parser.go b/pkg/graph/parser/parser.go
index 6e4a9ef..557d6e0 100644
--- a/pkg/graph/parser/parser.go
+++ b/pkg/graph/parser/parser.go
@@ -68,59 +68,94 @@ func parseResource(resource interface{}, schema *spec.Schema, path string) ([]va
 }
 
 func getExpectedTypes(schema *spec.Schema) ([]string, error) {
-	// Handle "x-kubernetes-preserve-unknown-fields" extension first
-	if hasStructuralSchemaMarkerEnabled(schema, xKubernetesPreserveUnknownFields) {
+	// Handle extensions (like x-kubernetes-int-or-string)
+	if types, found := handleSchemaExtensions(schema); found {
+		return types, nil
+	}
+
+	// Handle composite schemas (like OneOf, AnyOf)
+	if types, found := handleCompositeSchemas(schema); found {
+		return types, nil
+	}
+
+	// Handle direct type definitions
+	if len(schema.Type) > 0 && schema.Type[0] != "" {
+		return schema.Type, nil
+	}
+
+	// Handle additional properties
+	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Allows {
+		// NOTE(a-hilaly): I don't like the type "any", we might want to change this to "object"
+		// in the future; just haven't really thought about it yet.
+		// Basically "any" means that the field can be of any type, and we have to check
+		// the ExpectedSchema field.
 		return []string{schemaTypeAny}, nil
 	}
 
+	return nil, fmt.Errorf("unknown schema type")
+}
+
+// handleSchemaExtensions processes Kubernetes-specific schema extensions
+// and returns appropriate types if extensions are present.
+func handleSchemaExtensions(schema *spec.Schema) ([]string, bool) {
+	// Handle "x-kubernetes-preserve-unknown-fields" extension
+	if hasStructuralSchemaMarkerEnabled(schema, xKubernetesPreserveUnknownFields) {
+		return []string{schemaTypeAny}, true
+	}
+
 	// Handle "x-kubernetes-int-or-string" extension
 	if hasStructuralSchemaMarkerEnabled(schema, xKubernetesIntOrString) {
-		return []string{"string", "integer"}, nil
+		return []string{"string", "integer"}, true
 	}
 
+	return nil, false
+}
+
+// handleCompositeSchemas processes OneOf and AnyOf schemas
+// and returns collected types if present.
+func handleCompositeSchemas(schema *spec.Schema) ([]string, bool) {
 	// Handle OneOf schemas
 	if len(schema.OneOf) > 0 {
-		var types []string
-
-		for _, subSchema := range schema.OneOf {
-			// If there are structural constraints, inject object
-			if len(subSchema.Required) > 0 || subSchema.Not != nil {
-				if !slices.Contains(types, "object") {
-					types = append(types, "object")
-				}
-			}
-			// Collect types if present
-			if len(subSchema.Type) > 0 {
-				types = append(types, subSchema.Type...)
-			}
-		}
-		// If we found any types, return them
+		types := collectTypesFromSubSchemas(schema.OneOf)
 		if len(types) > 0 {
-			return types, nil
+			return types, true
 		}
 	}
 
 	// Handle AnyOf schemas
 	if len(schema.AnyOf) > 0 {
-		var types []string
-		for _, subType := range schema.AnyOf {
-			types = append(types, subType.Type...)
+		types := collectTypesFromSubSchemas(schema.AnyOf)
+		if len(types) > 0 {
+			return types, true
 		}
-		return types, nil
 	}
 
-	if len(schema.Type) > 0 && schema.Type[0] != "" {
-		return schema.Type, nil
-	}
+	return nil, false
+}
 
-	if schema.AdditionalProperties != nil && schema.AdditionalProperties.Allows {
-		// NOTE(a-hilaly): I don't like the type "any", we might want to change this to "object"
-		// in the future; just haven't really thought about it yet.
-		// Basically "any" means that the field can be of any type, and we have to check
-		// the ExpectedSchema field.
-		return []string{schemaTypeAny}, nil
+// collectTypesFromSubSchemas extracts types from a slice of schemas,
+// handling structural constraints like Required and Not.
+func collectTypesFromSubSchemas(subSchemas []spec.Schema) []string {
+	var types []string
+
+	for _, subSchema := range subSchemas {
+		// If there are structural constraints, inject object type
+		if len(subSchema.Required) > 0 || subSchema.Not != nil {
+			if !slices.Contains(types, "object") {
+				types = append(types, "object")
+			}
+		}
+		// Collect types if present
+		if len(subSchema.Type) > 0 {
+			for _, t := range subSchema.Type {
+				if t != "" && !slices.Contains(types, t) {
+					types = append(types, t)
+				}
+			}
+		}
 	}
-	return nil, fmt.Errorf("unknown schema type")
+
+	return types
 }
 
 func validateSchema(schema *spec.Schema, path string) error {
@@ -236,7 +271,7 @@ func parseScalarTypes(field interface{}, _ *spec.Schema, path string, expectedTy
 	// perform type checks for scalar types
 	switch {
 	case slices.Contains(expectedTypes, "number"):
-		if _, ok := field.(float64); !ok {
+		if !isNumber(field) {
 			return nil, fmt.Errorf("expected number type for path %s, got %T", path, field)
 		}
 	case slices.Contains(expectedTypes, "int"), slices.Contains(expectedTypes, "integer"):
@@ -286,9 +321,22 @@ func getArrayItemSchema(schema *spec.Schema, path string) (*spec.Schema, error)
 	return nil, fmt.Errorf("invalid array schema for path %s: neither Items.Schema nor Properties are defined", path)
 }
 
+func isNumber(v interface{}) bool {
+	return isInteger(v) || isFloat(v)
+}
+
+func isFloat(v interface{}) bool {
+	switch v.(type) {
+	case float32, float64:
+		return true
+	default:
+		return false
+	}
+}
+
 func isInteger(v interface{}) bool {
 	switch v.(type) {
-	case int, int64, int32:
+	case int, int8, int32, int64:
 		return true
 	default:
 		return false
diff --git a/pkg/metadata/groupversion.go b/pkg/metadata/groupversion.go
index 458aa94..89c0f00 100644
--- a/pkg/metadata/groupversion.go
+++ b/pkg/metadata/groupversion.go
@@ -71,7 +71,7 @@ func GetResourceGraphDefinitionInstanceGVK(group, apiVersion, kind string) schem
 func GetResourceGraphDefinitionInstanceGVR(group, apiVersion, kind string) schema.GroupVersionResource {
 	pluralKind := flect.Pluralize(strings.ToLower(kind))
 	return schema.GroupVersionResource{
-		Group:    fmt.Sprintf("%s.%s", pluralKind, group),
+		Group:    group,
 		Version:  apiVersion,
 		Resource: pluralKind,
 	}
diff --git a/pkg/metadata/labels.go b/pkg/metadata/labels.go
index 6efee9f..34b106e 100644
--- a/pkg/metadata/labels.go
+++ b/pkg/metadata/labels.go
@@ -117,7 +117,6 @@ func NewResourceGraphDefinitionLabeler(rgMeta metav1.Object) GenericLabeler {
 	return map[string]string{
 		ResourceGraphDefinitionIDLabel:        string(rgMeta.GetUID()),
 		ResourceGraphDefinitionNameLabel:      rgMeta.GetName(),
-		ResourceGraphDefinitionNamespaceLabel: rgMeta.GetNamespace(),
 	}
 }
 
diff --git a/pkg/simpleschema/markers.go b/pkg/simpleschema/markers.go
index 9eaf3dd..d422b48 100644
--- a/pkg/simpleschema/markers.go
+++ b/pkg/simpleschema/markers.go
@@ -46,11 +46,16 @@ const (
 	MarkerTypeDefault MarkerType = "default"
 	// MarkerTypeDescription represents the `description` marker.
 	MarkerTypeDescription MarkerType = "description"
+	// MarkerTypeMinimum represents the `minimum` marker.
+	MarkerTypeMinimum MarkerType = "minimum"
+	// MarkerTypeMaximum represents the `maximum` marker.
+	MarkerTypeMaximum MarkerType = "maximum"
 )
 
 func markerTypeFromString(s string) (MarkerType, error) {
 	switch MarkerType(s) {
-	case MarkerTypeRequired, MarkerTypeDefault, MarkerTypeDescription:
+	case MarkerTypeRequired, MarkerTypeDefault, MarkerTypeDescription,
+		MarkerTypeMinimum, MarkerTypeMaximum:
 		return MarkerType(s), nil
 	default:
 		return "", fmt.Errorf("unknown marker type: %s", s)
diff --git a/pkg/simpleschema/transform.go b/pkg/simpleschema/transform.go
index df12c58..7218e76 100644
--- a/pkg/simpleschema/transform.go
+++ b/pkg/simpleschema/transform.go
@@ -15,6 +15,7 @@ package simpleschema
 
 import (
 	"fmt"
+	"strconv"
 
 	extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 )
@@ -203,6 +204,14 @@ func (tf *transformer) applyMarkers(schema *extv1.JSONSchemaProps, markers []*Ma
 			schema.Default = &extv1.JSON{Raw: defaultValue}
 		case MarkerTypeDescription:
 			schema.Description = marker.Value
+		case MarkerTypeMinimum:
+			if val, err := strconv.ParseFloat(marker.Value, 64); err == nil {
+				schema.Minimum = &val
+			}
+		case MarkerTypeMaximum:
+			if val, err := strconv.ParseFloat(marker.Value, 64); err == nil {
+				schema.Maximum = &val
+			}
 		}
 	}
 }
diff --git a/website/docs/docs/concepts/00-resource-group-definitions.md b/website/docs/docs/concepts/00-resource-group-definitions.md
index 89884a3..a2c69a0 100644
--- a/website/docs/docs/concepts/00-resource-group-definitions.md
+++ b/website/docs/docs/concepts/00-resource-group-definitions.md
@@ -153,11 +153,11 @@ its behavior accordingly.
 
 After the **ResourceGraphDefinition** is validated and registered in the cluster, users
 can can create instances of it. Here's an example of how an instance for the
-`SimpleWebApp` might look:
+`WebApplication` might look:
 
 ```yaml title="my-web-app-instance.yaml"
 apiVersion: kro.run/v1alpha1
-kind: SimpleWebApp
+kind: WebApplication
 metadata:
   name: my-web-app
 spec:
diff --git a/website/docs/docs/getting-started/01-Installation.md b/website/docs/docs/getting-started/01-Installation.md
index b159df0..11c9a9c 100644
--- a/website/docs/docs/getting-started/01-Installation.md
+++ b/website/docs/docs/getting-started/01-Installation.md
@@ -46,6 +46,13 @@ helm install kro oci://ghcr.io/kro-run/kro/kro \
   --version=${KRO_VERSION}
 ```
 
+:::info[**Troubleshooting Helm Install**]
+Note that authentication is not required for pulling charts from public GHCR (GitHub Container Registry) repositories.
+
+Helm install download failures occur due to expired local credentials. To resolve this issue, clear your local credentials cache by running `helm registry logout ghcr.io` in your terminal, then retry the installation.
+
+:::
+
 ## Verifying the Installation
 
 After running the installation command, verify that Kro has been installed
diff --git a/website/docs/docs/overview.md b/website/docs/docs/overview.md
index ca91e83..ca592fb 100644
--- a/website/docs/docs/overview.md
+++ b/website/docs/docs/overview.md
@@ -43,7 +43,7 @@ _Fugure 1: End user interface - Custom API_
 
 When you install **Kro** in your cluster, it installs a Custom Resource
 Definition (CRD) called **ResourceGraphDefinition (RG)**. The **Platform**, **Security**,
-and **Compliance** teams, can collaborate to create custom APIs by defining
+and **Compliance** teams can collaborate to create custom APIs by defining
 Custom Resources for the ResourceGraphDefinition CRD.
 
 In the depicted example, the **Platform Team** has created a **RG** with
diff --git a/website/docs/examples/aws/aws-examples.md b/website/docs/examples/aws/aws-examples.md
index d977b36..71c1709 100644
--- a/website/docs/examples/aws/aws-examples.md
+++ b/website/docs/examples/aws/aws-examples.md
@@ -2,6 +2,10 @@
 sidebar_position: 301
 ---
 
+:::info[**ACK controllers Dependency**]
+Most examples below will need ACK controllers to be running in the cluster either through kro or other means.
+:::
+
 # AWS Examples
 - [Deploying a Controller](./deploying-controller.md) Discover how to deploy a
   Kubernetes controller using kro ResourceGraphDefinitions, including the necessary



Test patch:
diff --git a/pkg/graph/emulator/emulator_test.go b/pkg/graph/emulator/emulator_test.go
index 327a6a3..d2918a5 100644
--- a/pkg/graph/emulator/emulator_test.go
+++ b/pkg/graph/emulator/emulator_test.go
@@ -501,6 +501,24 @@ func TestGenerateValueWithIntOrString(t *testing.T) {
 
 }
 
+func TestGenerateValueWithPreserveUnknownFields(t *testing.T) {
+	e := NewEmulator()
+
+	t.Run("x-kubernetes-preserve-unknown-fields present, does not produce error", func(t *testing.T) {
+		schema := &spec.Schema{
+			VendorExtensible: spec.VendorExtensible{
+				Extensions: map[string]interface{}{
+					"x-kubernetes-preserve-unknown-fields": true,
+				},
+			},
+		}
+
+		_, err := e.generateValue(schema)
+		require.NoError(t, err)
+	})
+
+}
+
 func ptr[T comparable](v T) *T {
 	return &v
 }
diff --git a/pkg/graph/parser/parser_test.go b/pkg/graph/parser/parser_test.go
index a02615f..bcceb08 100644
--- a/pkg/graph/parser/parser_test.go
+++ b/pkg/graph/parser/parser_test.go
@@ -1495,3 +1495,83 @@ func TestPreserveUnknownFields(t *testing.T) {
 		})
 	}
 }
+
+func TestCollectTypesFromSubSchemas(t *testing.T) {
+	testCases := []struct {
+		name       string
+		subSchemas []spec.Schema
+		wantTypes  []string
+	}{
+		{
+			name: "simple types without constraints",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{Type: []string{"string"}}},
+				{SchemaProps: spec.SchemaProps{Type: []string{"integer"}}},
+			},
+			wantTypes: []string{"string", "integer"},
+		},
+		{
+			name: "with Required constraint",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{
+					Type:     []string{"string"},
+					Required: []string{"field"},
+				}},
+			},
+			wantTypes: []string{"object", "string"},
+		},
+		{
+			name: "with Not constraint",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{
+					Type: []string{"string"},
+					Not: &spec.Schema{
+						SchemaProps: spec.SchemaProps{Type: []string{"integer"}},
+					},
+				}},
+			},
+			wantTypes: []string{"object", "string"},
+		},
+		{
+			name: "with both Required and Not constraints",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{
+					Type:     []string{"string"},
+					Required: []string{"field"},
+					Not: &spec.Schema{
+						SchemaProps: spec.SchemaProps{Type: []string{"integer"}},
+					},
+				}},
+			},
+			wantTypes: []string{"object", "string"},
+		},
+		{
+			name: "duplicate types",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{Type: []string{"string"}}},
+				{SchemaProps: spec.SchemaProps{Type: []string{"string"}}},
+				{SchemaProps: spec.SchemaProps{
+					Type:     []string{"string"},
+					Required: []string{"field"},
+				}},
+			},
+			wantTypes: []string{"object", "string"},
+		},
+		{
+			name: "empty type",
+			subSchemas: []spec.Schema{
+				{SchemaProps: spec.SchemaProps{Type: []string{""}}},
+			},
+			wantTypes: []string{},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			gotTypes := collectTypesFromSubSchemas(tc.subSchemas)
+			if !areEqualSlices(gotTypes, tc.wantTypes) {
+				t.Errorf("collectTypesFromSubSchemas() = %v, want %v", gotTypes, tc.wantTypes)
+			}
+		})
+	}
+}
diff --git a/pkg/simpleschema/field_test.go b/pkg/simpleschema/field_test.go
index a8f8984..33840a5 100644
--- a/pkg/simpleschema/field_test.go
+++ b/pkg/simpleschema/field_test.go
@@ -51,6 +51,26 @@ func TestParseFieldSchema(t *testing.T) {
 			wantMarkers: nil,
 			wantErr:     true,
 		},
+		{
+			name:        "integer field with min and max",
+			fieldSchema: "integer | minimum=0 maximum=100",
+			wantType:    "integer",
+			wantMarkers: []*Marker{
+				{MarkerType: MarkerTypeMinimum, Key: "minimum", Value: "0"},
+				{MarkerType: MarkerTypeMaximum, Key: "maximum", Value: "100"},
+			},
+			wantErr: false,
+		},
+		{
+			name:        "number field with decimal constraints",
+			fieldSchema: "float | minimum=0.1 maximum=1.0",
+			wantType:    "float",
+			wantMarkers: []*Marker{
+				{MarkerType: MarkerTypeMinimum, Key: "minimum", Value: "0.1"},
+				{MarkerType: MarkerTypeMaximum, Key: "maximum", Value: "1.0"},
+			},
+			wantErr: false,
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/simpleschema/markers_test.go b/pkg/simpleschema/markers_test.go
index cacf57f..0b63d26 100644
--- a/pkg/simpleschema/markers_test.go
+++ b/pkg/simpleschema/markers_test.go
@@ -86,6 +86,24 @@ func TestParseMarkers(t *testing.T) {
 			},
 			wantErr: false,
 		},
+		{
+			name:  "minimum and maximum markers",
+			input: "minimum=0 maximum=100",
+			want: []*Marker{
+				{MarkerType: MarkerTypeMinimum, Key: "minimum", Value: "0"},
+				{MarkerType: MarkerTypeMaximum, Key: "maximum", Value: "100"},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "decimal minimum and maximum",
+			input: "minimum=0.1 maximum=1.5",
+			want: []*Marker{
+				{MarkerType: MarkerTypeMinimum, Key: "minimum", Value: "0.1"},
+				{MarkerType: MarkerTypeMaximum, Key: "maximum", Value: "1.5"},
+			},
+			wantErr: false,
+		},
 		{
 			name:  "Markers with spaces in values",
 			input: "description=\"This has spaces\" default=5 required=true",