Define the CartsStack RGD
Let's write the CartsStack RGD, the blueprint introduced on the previous page. Its two key sections are the schema (the inputs a user provides) and the resources (the graph kro creates from them):
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
name: cartsstack
spec:
schema:
apiVersion: v1alpha1
kind: CartsStack
group: kro.run
spec:
tableName: string | required=true
namespace: string | required=true
image: string | default="public.ecr.aws/aws-containers/retail-store-sample-cart:1.2.1"
replicas: integer | default=1
status:
tableArn: ${table.status.ackResourceMetadata.arn}
resources:
- id: ns
template:
apiVersion: v1
kind: Namespace
metadata:
name: ${schema.spec.namespace}
- id: table
template:
apiVersion: dynamodb.services.k8s.aws/v1alpha1
kind: Table
metadata:
name: items
namespace: ${schema.spec.namespace}
spec:
tableName: ${schema.spec.tableName}
billingMode: PAY_PER_REQUEST
attributeDefinitions:
- attributeName: id
attributeType: "S"
- attributeName: customerId
attributeType: "S"
keySchema:
- attributeName: id
keyType: HASH
globalSecondaryIndexes:
- indexName: idx_global_customerId
keySchema:
- attributeName: customerId
keyType: HASH
- attributeName: id
keyType: RANGE
projection:
projectionType: "ALL"
- id: sa
template:
apiVersion: v1
kind: ServiceAccount
metadata:
name: carts
namespace: ${schema.spec.namespace}
- id: config
template:
apiVersion: v1
kind: ConfigMap
metadata:
name: carts
namespace: ${schema.spec.namespace}
data:
RETAIL_CART_PERSISTENCE_PROVIDER: dynamodb
RETAIL_CART_PERSISTENCE_DYNAMODB_TABLE_NAME: ${schema.spec.tableName}
- id: deployment
template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: carts
namespace: ${schema.spec.namespace}
labels:
app.kubernetes.io/created-by: eks-workshop
app.kubernetes.io/type: app
spec:
replicas: ${schema.spec.replicas}
selector:
matchLabels:
app.kubernetes.io/name: carts
app.kubernetes.io/instance: carts
app.kubernetes.io/component: service
template:
metadata:
labels:
app.kubernetes.io/name: carts
app.kubernetes.io/instance: carts
app.kubernetes.io/component: service
app.kubernetes.io/created-by: eks-workshop
spec:
serviceAccountName: ${sa.metadata.name}
securityContext:
fsGroup: 1000
containers:
- name: carts
image: ${schema.spec.image}
imagePullPolicy: IfNotPresent
env:
- name: JAVA_OPTS
value: -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom
envFrom:
- configMapRef:
name: ${config.metadata.name}
ports:
- name: http
containerPort: 8080
protocol: TCP
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 15
periodSeconds: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 45
periodSeconds: 3
resources:
limits:
memory: 1Gi
requests:
cpu: 250m
memory: 1Gi
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-volume
volumes:
- name: tmp-volume
emptyDir:
medium: Memory
- id: service
template:
apiVersion: v1
kind: Service
metadata:
name: carts
namespace: ${schema.spec.namespace}
labels:
app.kubernetes.io/created-by: eks-workshop
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: carts
app.kubernetes.io/instance: carts
app.kubernetes.io/component: service
spec.schema declares the shape of the user-facing CR using kro's SimpleSchema syntax (the string | required=true form), not raw OpenAPI. Optional fields like image and replicas get defaults, so a minimal instance only sets tableName and namespace.
spec.resources is the graph kro creates. Each entry has an id (used to reference its outputs from other resources) and a template (the actual manifest). Note the ${schema.spec.X} references pulling in the user's inputs, and ${table.status...arn} / ${sa.metadata.name} references between resources, which is how kro infers the order to create them in.
Apply the RGD:
resourcegraphdefinition.kro.run/cartsstack created
kro validates the RGD synchronously: it type-checks every ${...} expression against the actual Kubernetes schemas of the resources you reference, and detects circular dependencies. If anything is wrong, the apply fails immediately with a descriptive error.
Wait for the RGD to reach Active. At this point kro has dynamically generated and registered the CartsStack CRD in the cluster:
resourcegraphdefinition.kro.run/cartsstack condition met
Confirm the new CartsStack kind is now a first-class Kubernetes API:
NAME SHORTNAMES APIVERSION NAMESPACED KIND
cartsstacks kro.run/v1alpha1 true CartsStack
cartsstacks is namespaced even though the RGD that defines it is cluster-scoped. RGDs live cluster-wide; instances always live in a specific namespace.
The schema exists. Next we'll apply an instance.