Skip to content

Commit f5a1194

Browse files
authored
Merge pull request #29334 from adellape/imp_golang
Edits to Golang tutorial
2 parents ad9f75d + f714159 commit f5a1194

File tree

5 files changed

+210
-11
lines changed

5 files changed

+210
-11
lines changed

modules/osdk-golang-implement-controller.adoc

Lines changed: 204 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,210 @@ After creating a new API and controller, you can implement the controller logic.
99

1010
.Procedure
1111

12-
* For this example, replace the generated controller file `controllers/memcached_controller.go` with the `memcached_controller.go` link:https://github.com/operator-framework/operator-sdk/blob/v1.3.0/testdata/go/v2/memcached-operator/controllers/memcached_controller.go[example implementation].
12+
* For this example, replace the generated controller file `controllers/memcached_controller.go` with following example implementation:
13+
+
14+
.Example `memcached_controller.go`
15+
[%collapsible]
16+
====
17+
[source,golang]
18+
----
19+
/*
20+
Copyright 2020.
21+
22+
Licensed under the Apache License, Version 2.0 (the "License");
23+
you may not use this file except in compliance with the License.
24+
You may obtain a copy of the License at
25+
26+
http://www.apache.org/licenses/LICENSE-2.0
27+
28+
Unless required by applicable law or agreed to in writing, software
29+
distributed under the License is distributed on an "AS IS" BASIS,
30+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31+
See the License for the specific language governing permissions and
32+
limitations under the License.
33+
*/
34+
35+
package controllers
36+
37+
import (
38+
appsv1 "k8s.io/api/apps/v1"
39+
corev1 "k8s.io/api/core/v1"
40+
"k8s.io/apimachinery/pkg/api/errors"
41+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
42+
"k8s.io/apimachinery/pkg/types"
43+
"reflect"
44+
45+
"context"
46+
47+
"github.com/go-logr/logr"
48+
"k8s.io/apimachinery/pkg/runtime"
49+
ctrl "sigs.k8s.io/controller-runtime"
50+
"sigs.k8s.io/controller-runtime/pkg/client"
51+
52+
cachev1alpha1 "github.com/example/memcached-operator/api/v1alpha1"
53+
)
54+
55+
// MemcachedReconciler reconciles a Memcached object
56+
type MemcachedReconciler struct {
57+
client.Client
58+
Log logr.Logger
59+
Scheme *runtime.Scheme
60+
}
61+
62+
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
63+
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
64+
// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
65+
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
66+
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
67+
68+
// Reconcile is part of the main kubernetes reconciliation loop which aims to
69+
// move the current state of the cluster closer to the desired state.
70+
// TODO(user): Modify the Reconcile function to compare the state specified by
71+
// the Memcached object against the actual cluster state, and then
72+
// perform operations to make the cluster state reflect the state specified by
73+
// the user.
74+
//
75+
// For more details, check Reconcile and its Result here:
76+
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
77+
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
78+
log := r.Log.WithValues("memcached", req.NamespacedName)
79+
80+
// Fetch the Memcached instance
81+
memcached := &cachev1alpha1.Memcached{}
82+
err := r.Get(ctx, req.NamespacedName, memcached)
83+
if err != nil {
84+
if errors.IsNotFound(err) {
85+
// Request object not found, could have been deleted after reconcile request.
86+
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
87+
// Return and don't requeue
88+
log.Info("Memcached resource not found. Ignoring since object must be deleted")
89+
return ctrl.Result{}, nil
90+
}
91+
// Error reading the object - requeue the request.
92+
log.Error(err, "Failed to get Memcached")
93+
return ctrl.Result{}, err
94+
}
95+
96+
// Check if the deployment already exists, if not create a new one
97+
found := &appsv1.Deployment{}
98+
err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
99+
if err != nil && errors.IsNotFound(err) {
100+
// Define a new deployment
101+
dep := r.deploymentForMemcached(memcached)
102+
log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
103+
err = r.Create(ctx, dep)
104+
if err != nil {
105+
log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
106+
return ctrl.Result{}, err
107+
}
108+
// Deployment created successfully - return and requeue
109+
return ctrl.Result{Requeue: true}, nil
110+
} else if err != nil {
111+
log.Error(err, "Failed to get Deployment")
112+
return ctrl.Result{}, err
113+
}
114+
115+
// Ensure the deployment size is the same as the spec
116+
size := memcached.Spec.Size
117+
if *found.Spec.Replicas != size {
118+
found.Spec.Replicas = &size
119+
err = r.Update(ctx, found)
120+
if err != nil {
121+
log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
122+
return ctrl.Result{}, err
123+
}
124+
// Spec updated - return and requeue
125+
return ctrl.Result{Requeue: true}, nil
126+
}
127+
128+
// Update the Memcached status with the pod names
129+
// List the pods for this memcached's deployment
130+
podList := &corev1.PodList{}
131+
listOpts := []client.ListOption{
132+
client.InNamespace(memcached.Namespace),
133+
client.MatchingLabels(labelsForMemcached(memcached.Name)),
134+
}
135+
if err = r.List(ctx, podList, listOpts...); err != nil {
136+
log.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name)
137+
return ctrl.Result{}, err
138+
}
139+
podNames := getPodNames(podList.Items)
140+
141+
// Update status.Nodes if needed
142+
if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
143+
memcached.Status.Nodes = podNames
144+
err := r.Status().Update(ctx, memcached)
145+
if err != nil {
146+
log.Error(err, "Failed to update Memcached status")
147+
return ctrl.Result{}, err
148+
}
149+
}
150+
151+
return ctrl.Result{}, nil
152+
}
153+
154+
// deploymentForMemcached returns a memcached Deployment object
155+
func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment {
156+
ls := labelsForMemcached(m.Name)
157+
replicas := m.Spec.Size
158+
159+
dep := &appsv1.Deployment{
160+
ObjectMeta: metav1.ObjectMeta{
161+
Name: m.Name,
162+
Namespace: m.Namespace,
163+
},
164+
Spec: appsv1.DeploymentSpec{
165+
Replicas: &replicas,
166+
Selector: &metav1.LabelSelector{
167+
MatchLabels: ls,
168+
},
169+
Template: corev1.PodTemplateSpec{
170+
ObjectMeta: metav1.ObjectMeta{
171+
Labels: ls,
172+
},
173+
Spec: corev1.PodSpec{
174+
Containers: []corev1.Container{{
175+
Image: "memcached:1.4.36-alpine",
176+
Name: "memcached",
177+
Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
178+
Ports: []corev1.ContainerPort{{
179+
ContainerPort: 11211,
180+
Name: "memcached",
181+
}},
182+
}},
183+
},
184+
},
185+
},
186+
}
187+
// Set Memcached instance as the owner and controller
188+
ctrl.SetControllerReference(m, dep, r.Scheme)
189+
return dep
190+
}
191+
192+
// labelsForMemcached returns the labels for selecting the resources
193+
// belonging to the given memcached CR name.
194+
func labelsForMemcached(name string) map[string]string {
195+
return map[string]string{"app": "memcached", "memcached_cr": name}
196+
}
197+
198+
// getPodNames returns the pod names of the array of pods passed in
199+
func getPodNames(pods []corev1.Pod) []string {
200+
var podNames []string
201+
for _, pod := range pods {
202+
podNames = append(podNames, pod.Name)
203+
}
204+
return podNames
205+
}
206+
207+
// SetupWithManager sets up the controller with the Manager.
208+
func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
209+
return ctrl.NewControllerManagedBy(mgr).
210+
For(&cachev1alpha1.Memcached{}).
211+
Owns(&appsv1.Deployment{}).
212+
Complete(r)
213+
}
214+
----
215+
====
13216
+
14217
The example controller runs the following reconciliation logic for each `Memcached` custom resource (CR):
15218
+
@@ -18,5 +221,3 @@ The example controller runs the following reconciliation logic for each `Memcach
18221
* Ensure that the deployment size is the same as specified by the `Memcached` CR spec.
19222
* Update the `Memcached` CR status with the names of the `memcached` pods.
20223
--
21-
22-
The next subsections explain how the controller watches resources and how the reconcile loop is triggered. You can skip these subsections to go directly to building and running the Operator.

modules/osdk-run-deployment.adoc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,6 @@ $ make deploy IMG=<registry>/<user>/<image_name>:<tag>
5858
----
5959
+
6060
By default, this command creates a namespace with the name of your Operator project in the form `<project_name>-system` and is used for the deployment. This command also installs the RBAC manifests from `config/rbac`.
61-
+
62-
[NOTE]
63-
====
64-
If you enabled webhooks in your deployments, you must have `cert-manager` already installed in the cluster or the `make deploy` command will fail when it attempts to create the `cert-manager` resources.
65-
====
6661

6762
. Verify that the Operator is running:
6863
+

operators/operator_sdk/ansible/osdk-ansible-tutorial.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ include::modules/osdk-create-cr.adoc[leveloffset=+1]
4040
[id="osdk-ansible-tutorial-addtl-resources"]
4141
== Additional resources
4242

43-
- See xref:../../../operators/operator_sdk/ansible/osdk-ansible-project-layout.adoc#osdk-ansible-project-layout_osdk-ansible-project-layout[Appendices] to learn about the project directory structures created by the Operator SDK.
43+
- See xref:../../../operators/operator_sdk/ansible/osdk-ansible-project-layout.adoc#osdk-ansible-project-layout[Project layout for Ansible-based Operators] to learn about the directory structures created by the Operator SDK.

operators/operator_sdk/golang/osdk-golang-tutorial.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ include::modules/osdk-golang-generate-crd.adoc[leveloffset=+2]
3131
include::modules/osdk-about-openapi-validation.adoc[leveloffset=+3]
3232

3333
include::modules/osdk-golang-implement-controller.adoc[leveloffset=+1]
34+
35+
The next subsections explain how the controller in the example implementation watches resources and how the reconcile loop is triggered. You can skip these subsections to go directly to xref:../../../operators/operator_sdk/golang/osdk-golang-tutorial.adoc#osdk-run-operator_osdk-golang-tutorial[Running the Operator].
36+
3437
include::modules/osdk-golang-controller-resources.adoc[leveloffset=+2]
3538
include::modules/osdk-golang-controller-configs.adoc[leveloffset=+2]
3639
include::modules/osdk-golang-controller-reconcile-loop.adoc[leveloffset=+2]
@@ -47,4 +50,4 @@ include::modules/osdk-create-cr.adoc[leveloffset=+1]
4750
[id="osdk-golang-tutorial-addtl-resources"]
4851
== Additional resources
4952

50-
- See xref:../../../operators/operator_sdk/golang/osdk-golang-project-layout.adoc#osdk-golang-project-layout_osdk-golang-project-layout[Appendices] to learn about the project directory structures created by the Operator SDK.
53+
- See xref:../../../operators/operator_sdk/golang/osdk-golang-project-layout.adoc#osdk-golang-project-layout[Project layout for Go-based Operators] to learn about the directory structures created by the Operator SDK.

operators/operator_sdk/helm/osdk-helm-tutorial.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ include::modules/osdk-create-cr.adoc[leveloffset=+1]
4242
[id="osdk-helm-tutorial-addtl-resources"]
4343
== Additional resources
4444

45-
- See xref:../../../operators/operator_sdk/helm/osdk-helm-project-layout.adoc#osdk-helm-project-layout_osdk-helm-project-layout[Appendices] to learn about the project directory structures created by the Operator SDK.
45+
- See xref:../../../operators/operator_sdk/helm/osdk-helm-project-layout.adoc#osdk-helm-project-layout[Project layout for Helm-based Operators] to learn about the directory structures created by the Operator SDK.

0 commit comments

Comments
 (0)