Lab 3.2: API Design for Database Operator
Related Lesson: Lesson 3.2: Designing Your API
Navigation: ← Previous Lab: Controller Runtime | Module Overview | Next Lab: Reconciliation Logic →
Objectives
- Design API for a PostgreSQL database operator
- Use kubebuilder markers for validation
- Generate CRD with proper schema
- Test API validation
Prerequisites
- Completion of Module 2
- Understanding of API design principles
- kubebuilder installed
Exercise 1: Initialize Database Operator Project
Task 1.1: Create Project
# Create new project
mkdir -p ~/postgres-operator
cd ~/postgres-operator
# Initialize kubebuilder project
kubebuilder init --domain example.com --repo github.com/example/postgres-operator
Task 1.2: Create Database API
# Create Database API
kubebuilder create api --group database --version v1 --kind Database
# When prompted:
# Create Resource [y/n]: y
# Create Controller [y/n]: y
Exercise 2: Design Database Spec
Task 2.1: Define DatabaseSpec
Edit api/v1/database_types.go:
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
)
// DatabaseSpec defines the desired state of Database
type DatabaseSpec struct {
// Image is the PostgreSQL image to use
// +kubebuilder:validation:Required
// +kubebuilder:default="postgres:14"
Image string `json:"image"`
// Replicas is the number of database replicas
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=10
// +kubebuilder:default=1
Replicas *int32 `json:"replicas,omitempty"`
// Storage is the storage configuration
Storage StorageSpec `json:"storage"`
// Resources are the resource requirements
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
// DatabaseName is the name of the database to create
// +kubebuilder:validation:Required
DatabaseName string `json:"databaseName"`
// Username is the database user
// +kubebuilder:validation:Required
Username string `json:"username"`
}
// StorageSpec defines storage configuration
type StorageSpec struct {
// Size is the storage size (e.g., "10Gi")
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^[0-9]+(Gi|Mi)$`
Size string `json:"size"`
// StorageClass is the storage class to use
StorageClass string `json:"storageClass,omitempty"`
}
Task 2.2: Define DatabaseStatus
// DatabaseStatus defines the observed state of Database
type DatabaseStatus struct {
// Phase is the current phase
// +kubebuilder:validation:Enum=Pending;Creating;Ready;Failed
Phase string `json:"phase,omitempty"`
// Ready indicates if the database is ready
Ready bool `json:"ready,omitempty"`
// Endpoint is the database endpoint
Endpoint string `json:"endpoint,omitempty"`
// SecretName is the name of the Secret containing database credentials
SecretName string `json:"secretName,omitempty"`
}
Task 2.3: Complete Database Type
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.replicas"
// +kubebuilder:printcolumn:name="Ready",type="boolean",JSONPath=".status.ready"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// Database is the Schema for the databases API
type Database struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DatabaseSpec `json:"spec,omitempty"`
Status DatabaseStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// DatabaseList contains a list of Database
type DatabaseList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Database `json:"items"`
}
func init() {
SchemeBuilder.Register(&Database{}, &DatabaseList{})
}
Exercise 3: Generate and Verify CRD
Task 3.1: Generate Code
# Generate code
make generate
# Generate manifests
make manifests
Task 3.2: Examine Generated CRD
# Check CRD was generated and verify validation rules
cat config/crd/bases/database.example.com_databases.yaml | head -100
Questions:
- Are validation rules present?
- Are default values set?
- Are print columns defined?
Exercise 4: Test API Validation
Task 4.1: Install CRD
# Install CRD
make install
# Verify
kubectl get crd databases.database.example.com
Task 4.2: Test Valid Resource
# Create valid Database resource
cat <<EOF | kubectl apply -f -
apiVersion: database.example.com/v1
kind: Database
metadata:
name: test-db
spec:
image: postgres:14
replicas: 1
databaseName: mydb
username: admin
storage:
size: 10Gi
EOF
# Verify it was created
kubectl get database test-db
Task 4.3: Test Invalid Resources
# Test missing required field
cat <<EOF | kubectl apply -f -
apiVersion: database.example.com/v1
kind: Database
metadata:
name: invalid-db
spec:
image: postgres:14
# Missing databaseName
EOF
# Should fail validation
# Test invalid replica count
cat <<EOF | kubectl apply -f -
apiVersion: database.example.com/v1
kind: Database
metadata:
name: invalid-replicas
spec:
image: postgres:14
replicas: 20 # Exceeds maximum
databaseName: mydb
username: admin
storage:
size: 10Gi
EOF
# Should fail validation
# Test invalid storage size
cat <<EOF | kubectl apply -f -
apiVersion: database.example.com/v1
kind: Database
metadata:
name: invalid-storage
spec:
image: postgres:14
databaseName: mydb
username: admin
storage:
size: invalid # Doesn't match pattern
EOF
# Should fail validation
Exercise 5: Test Print Columns
Task 5.1: Create Multiple Databases
# Create a few databases
kubectl apply -f - <<EOF
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db1
spec:
image: postgres:14
replicas: 1
databaseName: db1
username: user1
storage:
size: 10Gi
---
apiVersion: database.example.com/v1
kind: Database
metadata:
name: db2
spec:
image: postgres:13
replicas: 2
databaseName: db2
username: user2
storage:
size: 20Gi
EOF
Task 5.2: Verify Print Columns
# List databases - should show print columns
kubectl get databases
# Should show: NAME, PHASE, REPLICAS, READY, AGE
Exercise 6: Test Default Values
Task 6.1: Create Resource with Minimal Spec
# Create with only required fields
cat <<EOF | kubectl apply -f -
apiVersion: database.example.com/v1
kind: Database
metadata:
name: minimal-db
spec:
databaseName: mydb
username: admin
storage:
size: 10Gi
# image and replicas should use defaults
EOF
Task 6.2: Verify Defaults
# Check if defaults were applied
kubectl get database minimal-db -o jsonpath='{.spec.image}'
kubectl get database minimal-db -o jsonpath='{.spec.replicas}'
Cleanup
# Delete test resources
kubectl delete databases --all
# Uninstall CRD
make uninstall
Lab Summary
In this lab, you:
- Designed a complete API for a database operator
- Used kubebuilder markers for validation
- Generated CRD with proper schema
- Tested API validation rules
- Verified print columns work
- Tested default values
Key Learnings
- API design follows Kubernetes conventions
- Spec contains desired state, Status contains actual state
- Validation markers enforce constraints
- Print columns improve user experience
- Default values make APIs easier to use
- Proper versioning is important
Solutions
The API design from this lab is used in the complete Database operator solution:
- Database Types - Complete API type definitions with validation markers
Next Steps
Now that you have a well-designed API, let’s implement the reconciliation logic to make it work!