Representing application state in the custom API

Let's begin by inspecting the generated api/v1alpha1/memcached_types.go file for our Memcached API:

cat api/v1alpha1/memcached_types.go

In Kubernetes, every object (with some exceptions, like ConfigMap) includes spec and status fields. Kubernetes functions by reconciling actual cluster state (status) with the desired state (spec).

Also notice the +kubebuilder comment markers found throughout the file. operator-sdk makes use of a tool called controller-gen from the controller-tools project to generate utility code and Kubernetes YAML. More information on markers for configuring code generation can be found in the kubebuilder markers reference.

Modify the MemcachedSpec and MemcachedStatus of the Memcached Custom Resource (CR) in the file api/v1alpha1/memcached_types.go

Change the boilerplate code to match the excerpt below:

package v1alpha1

import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.

// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
	// +kubebuilder:validation:Minimum=0
	// Size is the size of the memcached deployment
	Size int32 `json:"size"`
}

// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
	// Nodes are the names of the memcached pods
	Nodes []string `json:"nodes"`
}

Add the +kubebuilder:subresource:status marker to add a status subresource to the CRD manifest so that the controller can update the CR status without changing the rest of the CR object:


// Memcached is the Schema for the memcacheds API
// +kubebuilder:printcolumn:JSONPath=".spec.size",name=Desired,type=string
// +kubebuilder:printcolumn:JSONPath=".status.nodes",name=Nodes,type=string
// +kubebuilder:subresource:status
type Memcached struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   MemcachedSpec   `json:"spec,omitempty"`
	Status MemcachedStatus `json:"status,omitempty"`
}

func init() {
	SchemeBuilder.Register(&Memcached{}, &MemcachedList{})
}

After modifying any *_types.go types definition file, run the following command to update the zz_generated.deepcopy.go file:

make generate

The makefile target generate will invoke controller-gen to update the api/v1alpha1/zz_generated.deepcopy.go file to ensure the custom API's Go type definitions implement the runtime.Object interface that anything of type Kind must implement.

Now the make manifests command can generate a customized CRD and YAML manifests for Operator objects.

The manifests target will invoke controller-gen to generate the CRD manifests at config/crd/bases/cache.example.com_memcacheds.yaml. Because of the comment markers, notice there is a newly generated CRD yaml that reflects the spec.size and status.nodes OpenAPI v3 schema validation and customized print columns.

cat config/crd/bases/cache.example.com_memcacheds.yaml

Deploy your Memcached Custom Resource Definition to a running Kubernetes cluster:

kubectl apply -f config/crd/bases/cache.example.com_memcacheds.yaml

Confirm the CRD was successfully created:

kubectl get crd memcacheds.cache.example.com -o yaml