How to create conversion webhook for my operator with operator-sdk

  1. Build and install operator-sdk with the latest commit
cd $GOPATH/src/github.com/operator-framework
git clone git@github.com:operator-framework/operator-sdk.git
cd operator-sdk
make install
which operator-sdk
kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml
mkdir $GOPATH/src/github.com/example
cd $GOPATH/src/github.com/example
mkdir memcached-operator
cd memcached-operator
git init
operator-sdk init --domain example.com --repo github.com/example/memcached-operator
operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource --controller
// 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"`
}
make generate
make manifests
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := ctrl.Log.WithValues("memcached", req.NamespacedName)
log.Info("Memcached resource has changed.")
latest := &cachev1alpha1.Memcached{}
err := r.Get(ctx, req.NamespacedName, latest)
if err != nil {
if errors.IsNotFound(err) {
log.Info("Memcached resource not found. Ignoring since object must be deleted")
return ctrl.Result{}, nil
}
// Error reading the object - requeue the request.
log.Error(err, "Failed to get Memcached")
return ctrl.Result{}, err
}

podNames := []string{"pod1", "pod2", "pod3"}
latest.Status.Nodes = podNames
err = r.Status().Update(ctx, latest)
if err != nil {
log.Error(err, "Failed to update Memcached status")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cachev1alpha1.Memcached{}).
WithOptions(controller.Options{MaxConcurrentReconciles: 2}).
Complete(r)
}
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;

func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
...
}
make manifests
apiVersion: v1
kind: Namespace
metadata:
name: memcached-sample
---
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
namespace: memcached-sample
spec:
size: 3
git add api
git add config
git add controllers
git add hack
git add main.go
git add Dockerfile
git add go.mod
git add go.sum
git add PROJECT
git add Makefile
git add .dockerignore
git add .gitignore
git commit -a
git checkout -b v1alpha1
export USER=<name>
make docker-build docker-push IMG=docker.io/$USER/memcached-operator:v0.0.1
make deploy IMG=docker.io/$USER/memcached-operator:v0.0.1
kubectl get deploy -n memcached-operator-system
kubectl logs -f deploy/memcached-operator-controller-manager -n memcached-operator-system -c manager
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
INFO    controllers.Memcached    Memcached resource has changed.    {"memcached": "memcached-sample/memcached-sample"}
kubectl get Memcached memcached-sample -n memcached-sample -oyaml
kubectl delete Memcached memcached-sample -n memcached-sample
make undeploy IMG=docker.io/$USER/memcached-operator:v0.0.1
git checkout master
operator-sdk create api --group cache --version v1beta1 --kind Memcached
// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
// +kubebuilder:validation:Minimum=0
// ReplicaSize is the size of the memcached deployment
ReplicaSize int32 `json:"replicaSize"`
}
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
// Nodes are the names of the memcached pods
Nodes []string `json:"nodes"`
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:storageversion

// Memcached is the Schema for the memcacheds API
type Memcached struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec MemcachedSpec `json:"spec,omitempty"`
Status MemcachedStatus `json:"status,omitempty"`
}
make generate
make manifests
apiVersion: v1
kind: Namespace
metadata:
name: memcached-sample
---
apiVersion: cache.example.com/v1beta1
kind: Memcached
metadata:
name: memcached-sample
namespace: memcached-sample
spec:
replicaSize: 3
operator-sdk create webhook --conversion --version v1beta1 --kind Memcached --group cache --force
package v1beta1

// Hub marks this type as a conversion hub.
func (*Memcached) Hub() {}
package v1alpha1

import (
"github.com/example/memcached-operator/api/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/conversion"
)

// ConvertTo converts this Memcached to the Hub version (vbeta1).
func (src *Memcached) ConvertTo(dstRaw conversion.Hub) error {
dst := dstRaw.(*v1beta1.Memcached)
dst.Spec.ReplicaSize = src.Spec.Size
dst.ObjectMeta = src.ObjectMeta
dst.Status.Nodes = src.Status.Nodes
return nil
}

// ConvertFrom converts from the Hub version (vbeta1) to this version.
func (dst *Memcached) ConvertFrom(srcRaw conversion.Hub) error {
src := srcRaw.(*v1beta1.Memcached)
dst.Spec.Size = src.Spec.ReplicaSize
dst.ObjectMeta = src.ObjectMeta
dst.Status.Nodes = src.Status.Nodes
return nil
}
make generate
make manifests
#- patches/webhook_in_memcacheds.yaml
#- patches/cainjection_in_memcacheds.yaml
#- ../webhook
#- ../certmanager
#- manager_webhook_patch.yaml
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service
- manifests.yaml
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: memcacheds.cache.example.com
spec:
conversion:
strategy: Webhook
webhook:
conversionReviewVersions: ["v1alpha1", "v1beta1"]
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
git add api
git add config
git commit -a
export USER=<name>
make docker-build docker-push IMG=docker.io/$USER/memcached-operator:v0.0.2
make deploy IMG=docker.io/$USER/memcached-operator:v0.0.2
kubectl get deploy -n memcached-operator-system
kubectl logs -f deploy/memcached-operator-controller-manager -n memcached-operator-system -c manager
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
kubectl get Memcached memcached-sample -n memcached-sample -oyaml
apiVersion: cache.example.com/v1beta1
kind: Memcached
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"cache.example.com/v1alpha1","kind":"Memcached","metadata":{"annotations":{},"name":"memcached-sample","namespace":"memcached-sample"},"spec":{"size":3}}
creationTimestamp: "2021-03-16T18:47:30Z"
generation: 1
name: memcached-sample
namespace: memcached-sample
resourceVersion: "1830"
selfLink: /apis/cache.example.com/v1beta1/namespaces/memcached-sample/memcacheds/memcached-sample
uid: 05c375f1-02c7-4550-ac6c-8f2149995ee7
spec:
replicaSize: 3
status:
nodes:
- pod1
- pod2
- pod3
git checkout master
// +build tools

package tools

import (
// Needed for the storage version too.
_ "knative.dev/pkg/apiextensions/storageversion/cmd/migrate"
)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: memcached-operator-post-install-job-role
rules:
# Storage version upgrader needs to be able to patch CRDs.
- apiGroups:
- "apiextensions.k8s.io"
resources:
- "customresourcedefinitions"
- "customresourcedefinitions/status"
verbs:
- "get"
- "list"
- "update"
- "patch"
- "watch"
# Our own resources we care about.
- apiGroups:
- "cache.example.com"
resources:
- "memcacheds"
verbs:
- "get"
- "list"
- "create"
- "update"
- "delete"
- "patch"
- "watch"
apiVersion: v1
kind: ServiceAccount
metadata:
name: memcached-operator-post-install-job
namespace: memcached-operator-system
---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: memcached-operator-post-install-job-role-binding
subjects:
- kind: ServiceAccount
name: memcached-operator-post-install-job
namespace: memcached-operator-system
roleRef:
kind: ClusterRole
name: memcached-operator-post-install-job-role
apiGroup: rbac.authorization.k8s.io
apiVersion: batch/v1
kind: Job
metadata:
name: storage-version-migration
namespace: memcached-operator-system
labels:
app: "storage-version-migration"
spec:
ttlSecondsAfterFinished: 600
backoffLimit: 10
template:
metadata:
labels:
app: "storage-version-migration"
spec:
serviceAccountName: memcached-operator-post-install-job
restartPolicy: OnFailure
containers:
- name: migrate
image: ko://github.com/example/memcached-operator/vendor/knative.dev/pkg/apiextensions/storageversion/cmd/migrate
args:
- "memcacheds.cache.example.com"
go mod vendor
ko resolve -f config/post-install -B -t 0.0.2
git add vendor
git add config
git commit -a
git checkout -b v1beta1
git checkout v1alpha1
make deploy IMG=docker.io/$USER/memcached-operator:v0.0.1
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
kubectl get Memcached memcached-sample -n memcached-sample -oyaml
git checkout v1beta1
make deploy IMG=docker.io/$USER/memcached-operator:v0.0.2
kubectl get crd memcacheds.cache.example.com -oyaml
status:
...
storedVersions:
- v1alpha1
- v1beta1
kubectl apply -f config/post-install
kubectl get crd memcacheds.cache.example.com -oyaml
status:
...
storedVersions:
- v1beta1
kubectl get Memcached memcached-sample -n memcached-sample -oyaml
apiVersion: cache.example.com/v1beta1
kind: Memcached
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"cache.example.com/v1alpha1","kind":"Memcached","metadata":{"annotations":{},"name":"memcached-sample","namespace":"memcached-sample"},"spec":{"size":3}}
creationTimestamp: "2021-03-16T20:31:39Z"
generation: 1
name: memcached-sample
namespace: memcached-sample
resourceVersion: "835"
selfLink: /apis/cache.example.com/v1beta1/namespaces/memcached-sample/memcacheds/memcached-sample
uid: 6f6baba7-0222-4969-ad31-e3cf0e8aff15
spec:
replicaSize: 3
status:
nodes:
- pod1
- pod2
- pod3

--

--

--

A Chinese software engineer, used to study in Belgium and currently working in US, as Knative & Tekton Operator Lead and Istio Operator Contributor.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

“HEAPS ,the cutest little data structure ever invented”

wsGIZA: Wrapped Staked GIZA

My First Experience with Augmented Reality

Two Sets of Eyes, Part 2: Which of these 5 methods of code review work best?

Moddroid.io — Center of free Android Mod files

Statements And Comments In Python

Automate Your Browser Tasks With Python

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Vincent Hou

Vincent Hou

A Chinese software engineer, used to study in Belgium and currently working in US, as Knative & Tekton Operator Lead and Istio Operator Contributor.

More from Medium

Install VSFTPD, APACHE, PHP 8.1, MARIA DB, Wordpress on DEBIAN 10 (Google Cloud)

App stream 2.0 & AWS console authentications with Active Directory Federation Services (ADFS)

Hybrid Cloud Monitoring with Instana and IBM Secure Tunnel Network

Postel’s Law in your Cloud Platform