forked from operator-framework/operator-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandler.go.tmpl
More file actions
158 lines (141 loc) · 4.15 KB
/
handler.go.tmpl
File metadata and controls
158 lines (141 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package stub
import (
"fmt"
"reflect"
"context"
v1alpha1 "github.com/example-inc/memcached-operator/pkg/apis/cache/v1alpha1"
"github.com/operator-framework/operator-sdk/pkg/sdk"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)
func NewHandler() sdk.Handler {
return &Handler{}
}
type Handler struct {
}
func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
switch o := event.Object.(type) {
case *v1alpha1.Memcached:
memcached := o
// Ignore the delete event since the garbage collector will clean up all secondary resources for the CR
// All secondary resources must have the CR set as their OwnerReference for this to be the case
if event.Deleted {
return nil
}
// Create the deployment if it doesn't exist
dep := deploymentForMemcached(memcached)
err := sdk.Create(dep)
if err != nil && !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("failed to create deployment: %v", err)
}
// Ensure the deployment size is the same as the spec
err = sdk.Get(dep)
if err != nil {
return fmt.Errorf("failed to get deployment: %v", err)
}
size := memcached.Spec.Size
if *dep.Spec.Replicas != size {
dep.Spec.Replicas = &size
err = sdk.Update(dep)
if err != nil {
return fmt.Errorf("failed to update deployment: %v", err)
}
}
// Update the Memcached status with the pod names
podList := podList()
labelSelector := labels.SelectorFromSet(labelsForMemcached(memcached.Name)).String()
listOps := &metav1.ListOptions{LabelSelector: labelSelector}
err = sdk.List(memcached.Namespace, podList, sdk.WithListOptions(listOps))
if err != nil {
return fmt.Errorf("failed to list pods: %v", err)
}
podNames := getPodNames(podList.Items)
if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
memcached.Status.Nodes = podNames
err := sdk.Update(memcached)
if err != nil {
return fmt.Errorf("failed to update memcached status: %v", err)
}
}
}
return nil
}
// deploymentForMemcached returns a memcached Deployment object
func deploymentForMemcached(m *v1alpha1.Memcached) *appsv1.Deployment {
ls := labelsForMemcached(m.Name)
replicas := m.Spec.Size
dep := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: m.Name,
Namespace: m.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: ls,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Image: "memcached:1.4.36-alpine",
Name: "memcached",
Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
Ports: []v1.ContainerPort{{
ContainerPort: 11211,
Name: "memcached",
}},
}},
},
},
},
}
addOwnerRefToObject(dep, asOwner(m))
return dep
}
// labelsForMemcached returns the labels for selecting the resources
// belonging to the given memcached CR name.
func labelsForMemcached(name string) map[string]string {
return map[string]string{"app": "memcached", "memcached_cr": name}
}
// addOwnerRefToObject appends the desired OwnerReference to the object
func addOwnerRefToObject(obj metav1.Object, ownerRef metav1.OwnerReference) {
obj.SetOwnerReferences(append(obj.GetOwnerReferences(), ownerRef))
}
// asOwner returns an OwnerReference set as the memcached CR
func asOwner(m *v1alpha1.Memcached) metav1.OwnerReference {
trueVar := true
return metav1.OwnerReference{
APIVersion: m.APIVersion,
Kind: m.Kind,
Name: m.Name,
UID: m.UID,
Controller: &trueVar,
}
}
// podList returns a v1.PodList object
func podList() *v1.PodList {
return &v1.PodList{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
}
}
// getPodNames returns the pod names of the array of pods passed in
func getPodNames(pods []v1.Pod) []string {
var podNames []string
for _, pod := range pods {
podNames = append(podNames, pod.Name)
}
return podNames
}