Status Hydration
Status hydration is the process by which TypeKro populates the status fields of your resource graphs with live data from the Kubernetes cluster. This enables real-time monitoring, dynamic cross-resource references, and intelligent orchestration based on actual cluster state.
Understanding Status Hydration
What is Status Hydration?
Status hydration transforms static status mappings into dynamic, live data by:
- Querying cluster state - Reading actual resource status from Kubernetes
- Evaluating CEL expressions - Computing dynamic values based on runtime data
- Resolving cross-references - Following resource relationships at runtime
- Updating status fields - Populating your resource graph's status with live data
// Status mapping definition (static)
const statusMappings = {
url: Cel.template('http://%s', service.status.loadBalancer.ingress[0].ip),
ready: Cel.expr(deployment.status.readyReplicas, '> 0'),
phase: deployment.status.phase
};
// After hydration (live data)
const hydratedStatus = {
url: "http://203.0.113.15", // Actual load balancer IP
ready: true, // Computed from actual replica count
phase: "Running" // Actual deployment phase
};
How Status Hydration Works
import { type } from 'arktype';
import { toResourceGraph, simpleDeployment, simpleService, Cel } from 'typekro';
const WebAppStatus = type({
url: 'string',
phase: 'string',
readyReplicas: 'number',
healthy: 'boolean'
});
const webApp = toResourceGraph(
{ name: 'webapp', schema: { spec: WebAppSpec, status: WebAppStatus } },
(schema) => ({
deployment: simpleDeployment({
name: schema.spec.name,
image: schema.spec.image,
replicas: schema.spec.replicas
}),
service: simpleService({
name: Cel.template('%s-service', schema.spec.name),
selector: { app: schema.spec.name },
ports: [{ port: 80, targetPort: 3000 }],
type: 'LoadBalancer'
})
}),
// Status builder - defines how to hydrate status
(schema, resources) => ({
// Direct field mapping
phase: resources.deployment.status.phase,
readyReplicas: resources.deployment.status.readyReplicas,
// Conditional logic with CEL
url: Cel.expr(
resources.service.status.loadBalancer.ingress,
'.size() > 0 ? "http://" + ',
resources.service.status.loadBalancer.ingress[0].ip,
': "pending"'
),
// Complex health computation
healthy: Cel.expr(
resources.deployment.status.readyReplicas,
'== ',
resources.deployment.spec.replicas,
'&& ',
resources.service.status.loadBalancer.ingress,
'.size() > 0'
)
})
);
Status Hydration Process
1. Resource Deployment
When resources are deployed, TypeKro tracks their metadata and references:
const factory = await webApp.factory('direct');
await factory.deploy({
name: 'my-webapp',
image: 'nginx:latest',
replicas: 3
});
2. Status Querying
TypeKro periodically queries Kubernetes for resource status:
// TypeKro internally executes queries like:
const deployment = await k8sApi.readNamespacedDeployment('my-webapp', 'default');
const service = await k8sApi.readNamespacedService('my-webapp-service', 'default');
// Extracts status information:
const deploymentStatus = {
phase: deployment.status.phase,
readyReplicas: deployment.status.readyReplicas,
replicas: deployment.status.replicas
};
3. CEL Expression Evaluation
Dynamic expressions are evaluated against live data:
// CEL expression: resources.deployment.status.readyReplicas > 0
const celEvaluator = new CelEvaluator();
const result = celEvaluator.evaluate(
'readyReplicas > 0',
{ readyReplicas: 3 } // Live data from cluster
);
// Result: true
4. Status Population
The final status object is assembled and made available:
const status = await factory.getStatus();
console.log(status);
// Output:
// {
// url: "http://203.0.113.15",
// phase: "Running",
// readyReplicas: 3,
// healthy: true
// }
Status Mapping Patterns
Simple Field Mapping
Direct mapping from resource status to graph status:
const statusBuilder = (schema, resources) => ({
// Direct field access
deploymentPhase: resources.deployment.status.phase,
serviceType: resources.service.spec.type,
readyReplicas: resources.deployment.status.readyReplicas,
// Nested field access
clusterIP: resources.service.status.clusterIP,
loadBalancerIP: resources.service.status.loadBalancer?.ingress?.[0]?.ip,
// Resource metadata
createdAt: resources.deployment.metadata.creationTimestamp,
labels: resources.deployment.metadata.labels
});
Conditional Status Logic
Use CEL expressions for dynamic status computation:
const statusBuilder = (schema, resources) => ({
// Simple boolean logic
ready: Cel.expr(resources.deployment.status.readyReplicas, '> 0'),
// Complex conditionals
phase: Cel.expr(
resources.deployment.status.readyReplicas,
'== 0 ? "Stopped" : ',
resources.deployment.status.readyReplicas,
'< ',
resources.deployment.spec.replicas,
'? "Scaling" : "Running"'
),
// Multi-resource health check
healthy: Cel.expr<boolean>(
resources.app.status.readyReplicas, ' > 0 && ',
resources.database.status.readyReplicas, ' > 0 && ',
resources.service.status.loadBalancer.ingress, '.size() > 0'
),
// Environment-based logic
accessMode: Cel.expr<string>(
'schema.spec.environment == "production" ? "external" : "internal"'
)
});
Aggregated Status
Combine information from multiple resources:
const microservicesStatus = (schema, resources) => ({
// Count ready services
readyServices: Cel.expr<number>(`
(resources.userService.status.readyReplicas > 0 ? 1 : 0) +
(resources.orderService.status.readyReplicas > 0 ? 1 : 0) +
(resources.paymentService.status.readyReplicas > 0 ? 1 : 0)
`),
// Total resource count
totalResources: Object.keys(resources).length,
// Service endpoints
endpoints: {
users: Cel.template(
'http://%s:3001/users',
resources.userService.status.clusterIP
),
orders: Cel.template(
'http://%s:3002/orders',
resources.orderService.status.clusterIP
),
payments: Cel.template(
'http://%s:3003/payments',
resources.paymentService.status.clusterIP
)
},
// Overall system health
systemHealth: Cel.expr<'healthy' | 'degraded'>(`
resources.userService.status.readyReplicas > 0 &&
resources.orderService.status.readyReplicas > 0 &&
resources.paymentService.status.readyReplicas > 0 ? "healthy" : "degraded"
`)
});
Time-Based Status
Include temporal information in status:
const statusBuilder = (schema, resources) => ({
// Uptime calculation
uptimeSeconds: Cel.expr<number>(`
(now() - timestamp("resources.deployment.metadata.creationTimestamp")) / duration("1s")
`),
// Age in human-readable format
age: Cel.expr<string>(`
duration(now() - timestamp("resources.deployment.metadata.creationTimestamp")).getHours() > 24 ?
string(duration(now() - timestamp("resources.deployment.metadata.creationTimestamp")).getHours() / 24) + " days" :
string(duration(now() - timestamp("resources.deployment.metadata.creationTimestamp")).getHours()) + " hours"
`),
// Last update timestamp
lastUpdated: new Date().toISOString(),
// Status since creation
stabilityPeriod: Cel.expr<'stable' | 'initializing'>(`
now() - timestamp("resources.deployment.metadata.creationTimestamp") > duration("5m") ? "stable" : "initializing"
`)
});
Advanced Status Hydration
Cross-Graph Status References
Reference status from other deployed graphs:
// Shared database graph
const databaseGraph = toResourceGraph(
{ name: 'shared-database', schema: { spec: DatabaseSpec, status: DatabaseStatus } },
(schema) => ({
database: simpleDeployment({
name: schema.spec.name,
image: 'postgres:15'
})
}),
(schema, resources) => ({
host: resources.database.status.podIP,
ready: Cel.expr(resources.database.status.readyReplicas, '> 0'),
version: '15.0'
})
);
// Application that references shared database status
const appGraph = toResourceGraph(
{ name: 'app-with-shared-db', schema: { spec: AppSpec, status: AppStatus } },
(schema) => ({
app: simpleDeployment({
name: schema.spec.name,
image: schema.spec.image,
env: {
DATABASE_HOST: schema.spec.database.host // External reference
}
})
}),
(schema, resources) => ({
appReady: Cel.expr(resources.app.status.readyReplicas, '> 0'),
// Reference external database status
databaseConnected: schema.spec.database.ready, // From external graph
// Combined readiness
fullyReady: Cel.expr<boolean>(`
resources.app.status.readyReplicas > 0 && schema.spec.database.ready == true
`)
})
);
Custom Status Evaluators
Create reusable status evaluation functions:
// Reusable health evaluator
function createHealthEvaluator(resources: any[]) {
return {
allHealthy: Cel.expr<boolean>(`
${resources.map(r => `${r.status.readyReplicas} > 0`).join(' && ')}
`),
healthyCount: Cel.expr<number>(`
${resources.map(r => `(${r.status.readyReplicas} > 0 ? 1 : 0)`).join(' + ')}
`),
healthPercentage: Cel.expr<number>(`
(${resources.map(r => `(${r.status.readyReplicas} > 0 ? 1 : 0)`).join(' + ')}) * 100 / ${resources.length}
`)
};
}
// Use in status builder
const statusBuilder = (schema, resources) => {
const services = [resources.userService, resources.orderService, resources.paymentService];
const health = createHealthEvaluator(services);
return {
...health,
phase: Cel.expr<'Ready' | 'Degraded' | 'Failed'>(`
health.healthPercentage >= 100 ? "Ready" : health.healthPercentage >= 50 ? "Degraded" : "Failed"
`)
};
};
Hierarchical Status
Build nested status structures:
const complexStatus = (schema, resources) => ({
// Top-level status
phase: Cel.expr(
resources.app.status.readyReplicas, '> 0 && ',
resources.database.status.readyReplicas, '> 0 ? "Running" : "Pending"'
),
// Component status
components: {
application: {
ready: Cel.expr(resources.app.status.readyReplicas, '> 0'),
replicas: {
desired: resources.app.spec.replicas,
ready: resources.app.status.readyReplicas,
available: resources.app.status.availableReplicas
},
endpoints: {
internal: Cel.template('http://%s:80', resources.appService.status.clusterIP),
external: Cel.template(
'http://%s',
resources.appService.status.loadBalancer.ingress[0].ip
)
}
},
database: {
ready: Cel.expr(resources.database.status.readyReplicas, '> 0'),
host: resources.databaseService.spec.clusterIP,
port: 5432,
connections: {
current: Cel.expr(resources.database.status.readyReplicas, '* 100'), // Mock calculation
max: Cel.expr(resources.database.status.readyReplicas, '* 200')
}
}
},
// Operational metrics
metrics: {
uptime: Cel.expr<number>(`
(now() - timestamp("resources.app.metadata.creationTimestamp")) / duration("1s")
`),
resourceUtilization: {
cpu: Cel.expr(resources.app.status.readyReplicas, '* 0.1'), // Mock calculation
memory: Cel.expr(resources.app.status.readyReplicas, '* 0.2')
}
}
});
Status Hydration in Different Deployment Modes
Direct Mode Hydration
In direct mode, TypeKro actively queries the cluster:
const factory = await graph.factory('direct', {
namespace: 'development',
statusUpdateInterval: 30000 // Update every 30 seconds
});
await factory.deploy(spec);
// Status is automatically hydrated
const status = await factory.getStatus();
console.log('Live status:', status);
// Listen for status updates
factory.onStatusUpdate((newStatus) => {
console.log('Status updated:', newStatus);
});
KRO Mode Hydration
In KRO mode, status hydration is handled by the KRO controller:
// Generated ResourceGraphDefinition includes status mappings as CEL expressions
const yaml = graph.toYaml(spec);
console.log(yaml);
// Output includes:
// status:
// url: ${service.status.loadBalancer.ingress[0].ip}
// ready: ${deployment.status.readyReplicas > 0}
GitOps Mode Hydration
Status is available through Kubernetes API after deployment:
# Deploy via GitOps
kubectl apply -f webapp-definition.yaml
kubectl apply -f webapp-instance.yaml
# Check status
kubectl get webapp my-webapp -o jsonpath='{.status}'
Status Hydration Performance
Optimization Strategies
- Selective Hydration: Only hydrate frequently accessed status fields
- Caching: Cache status data to reduce API calls
- Batching: Batch multiple resource queries
- Incremental Updates: Only update changed fields
const optimizedStatus = (schema, resources) => ({
// Critical status (updated frequently)
ready: Cel.expr(resources.app.status.readyReplicas, '> 0'),
phase: resources.app.status.phase,
// Detailed status (cached)
details: {
lastUpdated: new Date().toISOString(),
// Expensive computation (cached for 5 minutes)
performanceMetrics: Cel.expr(
`"cached_until_" + string(now() + duration("5m"))`
)
}
});
Monitoring Status Hydration
const factory = await graph.factory('direct', {
statusUpdateInterval: 10000,
onStatusHydrationError: (error) => {
console.error('Status hydration failed:', error);
},
onStatusHydrationComplete: (duration) => {
console.log(`Status hydration completed in ${duration}ms`);
}
});
Error Handling
Status Hydration Failures
const robustStatus = (schema, resources) => ({
// Safe field access with fallbacks
phase: resources.deployment.status?.phase || 'Unknown',
// Safe CEL expressions with error handling
ready: Cel.expr(
resources.deployment.status?.readyReplicas || 0,
'> 0'
),
// Conditional status based on resource availability
url: Cel.expr(
resources.service.status?.loadBalancer?.ingress,
'.size() > 0 ? "http://" + ',
resources.service.status?.loadBalancer?.ingress?.[0]?.ip || '"unavailable"',
': "pending"'
),
// Error tracking
lastError: null,
lastSuccessfulUpdate: new Date().toISOString()
});
Retry Logic
const factory = await graph.factory('direct', {
statusUpdateRetries: 3,
statusUpdateBackoff: 'exponential',
statusUpdateTimeout: 30000
});
Debugging Status Hydration
Enable Debug Logging
import { createLogger } from 'typekro';
const logger = createLogger({
level: 'debug',
pretty: true
});
const factory = await graph.factory('direct', {
logger,
debugStatusHydration: true
});
Status Hydration Inspection
// Get raw resource status
const rawStatus = await factory.getRawResourceStatus();
console.log('Raw Kubernetes resource status:', rawStatus);
// Get status evaluation trace
const trace = await factory.getStatusEvaluationTrace();
console.log('CEL evaluation trace:', trace);
// Validate status mappings
const validation = await factory.validateStatusMappings();
console.log('Status mapping validation:', validation);
Best Practices
1. Design Meaningful Status
// ✅ Provide actionable status information
const meaningfulStatus = (schema, resources) => ({
// High-level state
phase: 'Running' | 'Pending' | 'Failed' | 'Scaling',
// Specific metrics
readyReplicas: resources.app.status.readyReplicas,
desiredReplicas: resources.app.spec.replicas,
// User-facing information
accessUrl: 'http://example.com',
// Operational data
lastDeployment: '2024-01-15T10:30:00Z',
// Health indicators
healthy: true,
issues: []
});
2. Handle Transient States
// ✅ Account for resource lifecycle states
const robustStatus = (schema, resources) => ({
phase: Cel.expr(
// Handle initialization phase
resources.deployment.status.readyReplicas,
'== 0 && ',
resources.deployment.status.replicas,
'== 0 ? "Initializing" : ',
// Handle scaling phases
resources.deployment.status.readyReplicas,
'< ',
resources.deployment.spec.replicas,
'? "Scaling" : "Ready"'
)
});
3. Provide Rollback Information
const statusWithHistory = (schema, resources) => ({
current: {
version: resources.deployment.metadata.labels?.version,
replicas: resources.deployment.status.readyReplicas
},
previous: {
version: resources.deployment.metadata.annotations?.['previous-version'],
rollbackAvailable: Cel.expr(
`"${resources.deployment.metadata.annotations?.['previous-version']}" != ""`
)
}
});
Next Steps
- CEL Expressions - Master dynamic status computation
- Cross-Resource References - Build interconnected status
- Direct Deployment - Deploy with live status hydration
- KRO Integration - Use KRO for advanced status orchestration