-
Notifications
You must be signed in to change notification settings - Fork 66
/
client.go
189 lines (164 loc) · 8.55 KB
/
client.go
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package service
import (
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
redisv1beta1 "github.com/ucloud/redis-operator/pkg/apis/redis/v1beta1"
"github.com/ucloud/redis-operator/pkg/client/k8s"
"github.com/ucloud/redis-operator/pkg/util"
)
// RedisClusterClient has the minimumm methods that a Redis cluster controller needs to satisfy
// in order to talk with K8s
type RedisClusterClient interface {
EnsureSentinelService(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureSentinelHeadlessService(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureSentinelConfigMap(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureSentinelProbeConfigMap(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureSentinelStatefulset(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureRedisStatefulset(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureRedisService(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureRedisShutdownConfigMap(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureRedisConfigMap(redisCluster *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error
EnsureNotPresentRedisService(redisCluster *redisv1beta1.RedisCluster) error
}
// RedisClusterKubeClient implements the required methods to talk with kubernetes
type RedisClusterKubeClient struct {
K8SService k8s.Services
logger logr.Logger
}
// NewRedisClusterKubeClient creates a new RedisClusterKubeClient
func NewRedisClusterKubeClient(k8sService k8s.Services, logger logr.Logger) *RedisClusterKubeClient {
return &RedisClusterKubeClient{
K8SService: k8sService,
logger: logger,
}
}
func generateSelectorLabels(component, name string) map[string]string {
return map[string]string{
"app.kubernetes.io/part-of": util.AppLabel,
"app.kubernetes.io/component": component,
"app.kubernetes.io/name": name,
}
}
// EnsureSentinelService makes sure the sentinel service exists
func (r *RedisClusterKubeClient) EnsureSentinelService(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
svc := generateSentinelService(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsService(rc.Namespace, svc)
}
// EnsureSentinelHeadlessService makes sure the sentinel headless service exists
func (r *RedisClusterKubeClient) EnsureSentinelHeadlessService(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
svc := newHeadLessSvcForCR(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsService(rc.Namespace, svc)
}
// EnsureSentinelConfigMap makes sure the sentinel configmap exists
func (r *RedisClusterKubeClient) EnsureSentinelConfigMap(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
cm := generateSentinelConfigMap(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsConfigMap(rc.Namespace, cm)
}
// EnsureSentinelConfigMap makes sure the sentinel configmap exists
func (r *RedisClusterKubeClient) EnsureSentinelProbeConfigMap(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
cm := generateSentinelReadinessProbeConfigMap(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsConfigMap(rc.Namespace, cm)
}
// EnsureSentinelStatefulset makes sure the sentinel deployment exists in the desired state
func (r *RedisClusterKubeClient) EnsureSentinelStatefulset(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
if err := r.ensurePodDisruptionBudget(rc, util.SentinelName, util.SentinelRoleName, labels, ownerRefs); err != nil {
return err
}
oldSs, err := r.K8SService.GetStatefulSet(rc.Namespace, util.GetSentinelName(rc))
if err != nil {
// If no resource we need to create.
if errors.IsNotFound(err) {
ss := generateSentinelStatefulSet(rc, labels, ownerRefs)
return r.K8SService.CreateStatefulSet(rc.Namespace, ss)
}
return err
}
if shouldUpdateRedis(rc.Spec.Sentinel.Resources, oldSs.Spec.Template.Spec.Containers[0].Resources, rc.Spec.Sentinel.Replicas, *oldSs.Spec.Replicas) {
ss := generateSentinelStatefulSet(rc, labels, ownerRefs)
return r.K8SService.UpdateStatefulSet(rc.Namespace, ss)
}
return nil
}
// EnsureRedisStatefulset makes sure the redis statefulset exists in the desired state
func (r *RedisClusterKubeClient) EnsureRedisStatefulset(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
if err := r.ensurePodDisruptionBudget(rc, util.RedisName, util.RedisRoleName, labels, ownerRefs); err != nil {
return err
}
oldSs, err := r.K8SService.GetStatefulSet(rc.Namespace, util.GetRedisName(rc))
if err != nil {
// If no resource we need to create.
if errors.IsNotFound(err) {
ss := generateRedisStatefulSet(rc, labels, ownerRefs)
return r.K8SService.CreateStatefulSet(rc.Namespace, ss)
}
return err
}
if shouldUpdateRedis(rc.Spec.Resources, oldSs.Spec.Template.Spec.Containers[0].Resources,
rc.Spec.Size, *oldSs.Spec.Replicas) {
ss := generateRedisStatefulSet(rc, labels, ownerRefs)
return r.K8SService.UpdateStatefulSet(rc.Namespace, ss)
}
return nil
}
func shouldUpdateRedis(expectResource, containterResource corev1.ResourceRequirements, expectSize, replicas int32) bool {
if expectSize != replicas {
return true
}
if result := containterResource.Requests.Cpu().Cmp(*expectResource.Requests.Cpu()); result != 0 {
return true
}
if result := containterResource.Requests.Memory().Cmp(*expectResource.Requests.Memory()); result != 0 {
return true
}
if result := containterResource.Limits.Cpu().Cmp(*expectResource.Limits.Cpu()); result != 0 {
return true
}
if result := containterResource.Limits.Memory().Cmp(*expectResource.Limits.Memory()); result != 0 {
return true
}
return false
}
// EnsureRedisConfigMap makes sure the sentinel configmap exists
func (r *RedisClusterKubeClient) EnsureRedisConfigMap(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
cm := generateRedisConfigMap(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsConfigMap(rc.Namespace, cm)
}
// EnsureRedisShutdownConfigMap makes sure the redis configmap with shutdown script exists
func (r *RedisClusterKubeClient) EnsureRedisShutdownConfigMap(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
if rc.Spec.ShutdownConfigMap != "" {
if _, err := r.K8SService.GetConfigMap(rc.Namespace, rc.Spec.ShutdownConfigMap); err != nil {
return err
}
} else {
cm := generateRedisShutdownConfigMap(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsConfigMap(rc.Namespace, cm)
}
return nil
}
// EnsureRedisService makes sure the redis statefulset exists
func (r *RedisClusterKubeClient) EnsureRedisService(rc *redisv1beta1.RedisCluster, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
svc := generateRedisService(rc, labels, ownerRefs)
return r.K8SService.CreateIfNotExistsService(rc.Namespace, svc)
}
// EnsureNotPresentRedisService makes sure the redis service is not present
func (r *RedisClusterKubeClient) EnsureNotPresentRedisService(rc *redisv1beta1.RedisCluster) error {
name := util.GetRedisName(rc)
namespace := rc.Namespace
// If the service exists (no get error), delete it
if _, err := r.K8SService.GetService(namespace, name); err == nil {
return r.K8SService.DeleteService(namespace, name)
}
return nil
}
// EnsureRedisStatefulset makes sure the pdb exists in the desired state
func (r *RedisClusterKubeClient) ensurePodDisruptionBudget(rc *redisv1beta1.RedisCluster, name string, component string, labels map[string]string, ownerRefs []metav1.OwnerReference) error {
name = util.GenerateName(name, rc.Name)
namespace := rc.Namespace
minAvailable := intstr.FromInt(2)
labels = util.MergeLabels(labels, generateSelectorLabels(component, rc.Name))
pdb := generatePodDisruptionBudget(name, namespace, labels, ownerRefs, minAvailable)
return r.K8SService.CreateIfNotExistsPodDisruptionBudget(namespace, pdb)
}