Skip to content

Commit 33ff880

Browse files
Merge pull request #1851 from openshift-cherrypick-robot/cherry-pick-1848-to-release-4.6
[release-4.6] Bug 1894163: Add spec hash to service's label to ensure service is correct
2 parents aba96a9 + a6a6e72 commit 33ff880

File tree

4 files changed

+100
-16
lines changed

4 files changed

+100
-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/configmap_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,24 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
347347
},
348348
},
349349
},
350+
{
351+
testName: "ExistingRegistry/BadServiceWithWrongHash",
352+
in: in{
353+
cluster: cluster{
354+
k8sObjs: append(setLabel(objectsForCatalogSource(validCatalogSource), &corev1.Service{}, ServiceHashLabelKey, "wrongHash"), validConfigMap),
355+
},
356+
catsrc: validCatalogSource,
357+
},
358+
out: out{
359+
status: &v1alpha1.RegistryServiceStatus{
360+
CreatedAt: now(),
361+
Protocol: "grpc",
362+
ServiceName: "cool-catalog",
363+
ServiceNamespace: testNamespace,
364+
Port: "50051",
365+
},
366+
},
367+
},
350368
{
351369
testName: "ExistingRegistry/BadPod",
352370
in: in{

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

pkg/controller/registry/reconciler/grpc_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ func TestGrpcRegistryReconciler(t *testing.T) {
114114
},
115115
},
116116
},
117+
{
118+
testName: "Grpc/ExistingRegistry/BadServiceWithWrongHash",
119+
in: in{
120+
cluster: cluster{
121+
k8sObjs: setLabel(objectsForCatalogSource(validGrpcCatalogSource("test-img", "")), &corev1.Service{}, ServiceHashLabelKey, "wrongHash"),
122+
},
123+
catsrc: validGrpcCatalogSource("test-img", ""),
124+
},
125+
out: out{
126+
status: &v1alpha1.RegistryServiceStatus{
127+
CreatedAt: now(),
128+
Protocol: "grpc",
129+
ServiceName: "img-catalog",
130+
ServiceNamespace: testNamespace,
131+
Port: "50051",
132+
},
133+
},
134+
},
117135
{
118136
testName: "Grpc/ExistingRegistry/BadService",
119137
in: in{

0 commit comments

Comments
 (0)