Skip to content

Commit 07c9c32

Browse files
committed
Add spec hash to service's label to ensure service is correct
Calculate hash from service spec and add it to service's labels. Will use this hash info to ensure service during catalog source reconciliation. This feature will force OLM to recreate new service if the hash doesn't match to avoid stale service with the same name. Signed-off-by: Vu Dinh <[email protected]>
1 parent 5eb1fa6 commit 07c9c32

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

pkg/controller/registry/reconciler/configmap.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ func (s *configMapCatalogSourceDecorator) Service() *v1.Service {
8585
Selector: s.Selector(),
8686
},
8787
}
88+
89+
labels := map[string]string{}
90+
hash := HashServiceSpec(svc.Spec)
91+
labels[ServiceHashLabelKey] = hash
92+
svc.SetLabels(labels)
8893
ownerutil.AddOwner(svc, s.CatalogSource, false, false)
8994
return svc
9095
}
@@ -363,8 +368,9 @@ func (c *ConfigMapRegistryReconciler) ensurePod(source configMapCatalogSourceDec
363368

364369
func (c *ConfigMapRegistryReconciler) ensureService(source configMapCatalogSourceDecorator, overwrite bool) error {
365370
service := source.Service()
366-
if c.currentService(source) != nil {
367-
if !overwrite {
371+
svc := c.currentService(source)
372+
if svc != nil {
373+
if !overwrite && ServiceHashMatch(svc, service) {
368374
return nil
369375
}
370376
if err := c.OpClient.DeleteService(service.GetNamespace(), service.GetName(), metav1.NewDeleteOptions(0)); err != nil {

pkg/controller/registry/reconciler/grpc.go

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@ package reconciler
33
import (
44
"context"
55
"fmt"
6+
"hash/fnv"
67
"time"
78

89
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
910

1011
"github.com/operator-framework/api/pkg/operators/v1alpha1"
12+
hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash"
1113
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
1214
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
1315
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1416
"github.com/pkg/errors"
1517
"github.com/sirupsen/logrus"
1618
corev1 "k8s.io/api/core/v1"
17-
v1 "k8s.io/api/core/v1"
1819
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1920
"k8s.io/apimachinery/pkg/labels"
2021
"k8s.io/apimachinery/pkg/util/intstr"
22+
"k8s.io/apimachinery/pkg/util/rand"
2123
)
2224

2325
const (
2426
CatalogSourceUpdateKey = "catalogsource.operators.coreos.com/update"
27+
ServiceHashLabelKey = "olm.service-spec-hash"
2528
CatalogPollingRequeuePeriod = 30 * time.Second
2629
)
2730

@@ -57,14 +60,14 @@ func (s *grpcCatalogSourceDecorator) Labels() map[string]string {
5760
}
5861
}
5962

60-
func (s *grpcCatalogSourceDecorator) Service() *v1.Service {
61-
svc := &v1.Service{
63+
func (s *grpcCatalogSourceDecorator) Service() *corev1.Service {
64+
svc := &corev1.Service{
6265
ObjectMeta: metav1.ObjectMeta{
6366
Name: s.GetName(),
6467
Namespace: s.GetNamespace(),
6568
},
66-
Spec: v1.ServiceSpec{
67-
Ports: []v1.ServicePort{
69+
Spec: corev1.ServiceSpec{
70+
Ports: []corev1.ServicePort{
6871
{
6972
Name: "grpc",
7073
Port: 50051,
@@ -74,11 +77,16 @@ func (s *grpcCatalogSourceDecorator) Service() *v1.Service {
7477
Selector: s.Labels(),
7578
},
7679
}
80+
81+
labels := map[string]string{}
82+
hash := HashServiceSpec(svc.Spec)
83+
labels[ServiceHashLabelKey] = hash
84+
svc.SetLabels(labels)
7785
ownerutil.AddOwner(svc, s.CatalogSource, false, false)
7886
return svc
7987
}
8088

81-
func (s *grpcCatalogSourceDecorator) Pod() *v1.Pod {
89+
func (s *grpcCatalogSourceDecorator) Pod() *corev1.Pod {
8290
pod := Pod(s.CatalogSource, "registry-server", s.Spec.Image, s.Labels(), 5, 10)
8391
ownerutil.AddOwner(pod, s.CatalogSource, false, false)
8492
return pod
@@ -93,7 +101,7 @@ type GrpcRegistryReconciler struct {
93101

94102
var _ RegistryReconciler = &GrpcRegistryReconciler{}
95103

96-
func (c *GrpcRegistryReconciler) currentService(source grpcCatalogSourceDecorator) *v1.Service {
104+
func (c *GrpcRegistryReconciler) currentService(source grpcCatalogSourceDecorator) *corev1.Service {
97105
serviceName := source.Service().GetName()
98106
service, err := c.Lister.CoreV1().ServiceLister().Services(source.GetNamespace()).Get(serviceName)
99107
if err != nil {
@@ -103,7 +111,7 @@ func (c *GrpcRegistryReconciler) currentService(source grpcCatalogSourceDecorato
103111
return service
104112
}
105113

106-
func (c *GrpcRegistryReconciler) currentPods(source grpcCatalogSourceDecorator) []*v1.Pod {
114+
func (c *GrpcRegistryReconciler) currentPods(source grpcCatalogSourceDecorator) []*corev1.Pod {
107115
pods, err := c.Lister.CoreV1().PodLister().Pods(source.GetNamespace()).List(source.Selector())
108116
if err != nil {
109117
logrus.WithError(err).Warn("couldn't find pod in cache")
@@ -115,7 +123,7 @@ func (c *GrpcRegistryReconciler) currentPods(source grpcCatalogSourceDecorator)
115123
return pods
116124
}
117125

118-
func (c *GrpcRegistryReconciler) currentUpdatePods(source grpcCatalogSourceDecorator) []*v1.Pod {
126+
func (c *GrpcRegistryReconciler) currentUpdatePods(source grpcCatalogSourceDecorator) []*corev1.Pod {
119127
pods, err := c.Lister.CoreV1().PodLister().Pods(source.GetNamespace()).List(source.SelectorForUpdate())
120128
if err != nil {
121129
logrus.WithError(err).Warn("couldn't find pod in cache")
@@ -127,13 +135,13 @@ func (c *GrpcRegistryReconciler) currentUpdatePods(source grpcCatalogSourceDecor
127135
return pods
128136
}
129137

130-
func (c *GrpcRegistryReconciler) currentPodsWithCorrectImage(source grpcCatalogSourceDecorator) []*v1.Pod {
138+
func (c *GrpcRegistryReconciler) currentPodsWithCorrectImage(source grpcCatalogSourceDecorator) []*corev1.Pod {
131139
pods, err := c.Lister.CoreV1().PodLister().Pods(source.GetNamespace()).List(labels.SelectorFromValidatedSet(source.Labels()))
132140
if err != nil {
133141
logrus.WithError(err).Warn("couldn't find pod in cache")
134142
return nil
135143
}
136-
found := []*v1.Pod{}
144+
found := []*corev1.Pod{}
137145
for _, p := range pods {
138146
if p.Spec.Containers[0].Image == source.Spec.Image {
139147
found = append(found, p)
@@ -262,8 +270,9 @@ func (c *GrpcRegistryReconciler) ensureUpdatePod(source grpcCatalogSourceDecorat
262270

263271
func (c *GrpcRegistryReconciler) ensureService(source grpcCatalogSourceDecorator, overwrite bool) error {
264272
service := source.Service()
265-
if c.currentService(source) != nil {
266-
if !overwrite {
273+
svc := c.currentService(source)
274+
if svc != nil {
275+
if !overwrite && ServiceHashMatch(svc, service) {
267276
return nil
268277
}
269278
if err := c.OpClient.DeleteService(service.GetNamespace(), service.GetName(), metav1.NewDeleteOptions(0)); err != nil {
@@ -274,6 +283,39 @@ func (c *GrpcRegistryReconciler) ensureService(source grpcCatalogSourceDecorator
274283
return err
275284
}
276285

286+
// ServiceHashMatch will check the hash info in existing Service to ensure its
287+
// hash info matches the desired Service's hash.
288+
func ServiceHashMatch(existing, new *corev1.Service) bool {
289+
labels := existing.GetLabels()
290+
newLabels := new.GetLabels()
291+
if len(labels) == 0 || len(newLabels) == 0 {
292+
return false
293+
}
294+
295+
existingSvcSpecHash, ok := labels[ServiceHashLabelKey]
296+
if !ok {
297+
return false
298+
}
299+
300+
newSvcSpecHash, ok := newLabels[ServiceHashLabelKey]
301+
if !ok {
302+
return false
303+
}
304+
305+
if existingSvcSpecHash != newSvcSpecHash {
306+
return false
307+
}
308+
309+
return true
310+
}
311+
312+
// HashServiceSpec calculates a hash given a copy of the service spec
313+
func HashServiceSpec(spec corev1.ServiceSpec) string {
314+
hasher := fnv.New32a()
315+
hashutil.DeepHashObject(hasher, &spec)
316+
return rand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
317+
}
318+
277319
// createUpdatePod is an internal method that creates a pod using the latest catalog source.
278320
func (c *GrpcRegistryReconciler) createUpdatePod(source grpcCatalogSourceDecorator) (*corev1.Pod, error) {
279321
// remove label from pod to ensure service does not accidentally route traffic to the pod
@@ -343,7 +385,7 @@ func (c *GrpcRegistryReconciler) CheckRegistryServer(catalogSource *v1alpha1.Cat
343385
// By updating the catalog on cluster it promotes the update pod to act as the new version of the catalog on-cluster.
344386
func (c *GrpcRegistryReconciler) promoteCatalog(updatePod *corev1.Pod, key string) error {
345387
// Update the update pod to promote it to serving pod via the SSA client
346-
err := c.SSAClient.Apply(context.TODO(), updatePod, func(p *v1.Pod) error {
388+
err := c.SSAClient.Apply(context.TODO(), updatePod, func(p *corev1.Pod) error {
347389
p.Labels[CatalogSourceLabelKey] = key
348390
p.Labels[CatalogSourceUpdateKey] = ""
349391
return nil

0 commit comments

Comments
 (0)