Skip to content

Commit 4fc476f

Browse files
authored
Add PriorityClass setting to registry pods for default CatalogSource (#2304)
The registry pods may need to have necessary priorityclass settings. OLM will set the priorityclass setting for registry pods by using the priorityclass annotations in the default catalogsources. Signed-off-by: Vu Dinh <[email protected]>
1 parent 734c6d0 commit 4fc476f

File tree

4 files changed

+132
-7
lines changed

4 files changed

+132
-7
lines changed

pkg/controller/registry/reconciler/configmap_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,9 @@ func objectsForCatalogSource(catsrc *v1alpha1.CatalogSource) []runtime.Object {
204204
if catsrc.Spec.Image != "" {
205205
decorated := grpcCatalogSourceDecorator{catsrc}
206206
objs = clientfake.AddSimpleGeneratedNames(
207-
decorated.Pod(""),
207+
decorated.Pod(catsrc.GetName()),
208208
decorated.Service(),
209+
decorated.ServiceAccount(),
209210
)
210211
}
211212
}

pkg/controller/registry/reconciler/grpc.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,16 @@ func (c *GrpcRegistryReconciler) currentUpdatePods(source grpcCatalogSourceDecor
171171
return pods
172172
}
173173

174-
func (c *GrpcRegistryReconciler) currentPodsWithCorrectImage(source grpcCatalogSourceDecorator) []*corev1.Pod {
174+
func (c *GrpcRegistryReconciler) currentPodsWithCorrectImageAndSpec(source grpcCatalogSourceDecorator, saName string) []*corev1.Pod {
175175
pods, err := c.Lister.CoreV1().PodLister().Pods(source.GetNamespace()).List(labels.SelectorFromValidatedSet(source.Labels()))
176176
if err != nil {
177177
logrus.WithError(err).Warn("couldn't find pod in cache")
178178
return nil
179179
}
180180
found := []*corev1.Pod{}
181+
newPod := source.Pod(saName)
181182
for _, p := range pods {
182-
if p.Spec.Containers[0].Image == source.Spec.Image {
183+
if p.Spec.Containers[0].Image == source.Spec.Image && podHashMatch(p, newPod) {
183184
found = append(found, p)
184185
}
185186
}
@@ -192,11 +193,12 @@ func (c *GrpcRegistryReconciler) EnsureRegistryServer(catalogSource *v1alpha1.Ca
192193

193194
// if service status is nil, we force create every object to ensure they're created the first time
194195
overwrite := source.Status.RegistryServiceStatus == nil
195-
// recreate the pod if no existing pod is serving the latest image
196-
overwritePod := overwrite || len(c.currentPodsWithCorrectImage(source)) == 0
197196

198197
//TODO: if any of these error out, we should write a status back (possibly set RegistryServiceStatus to nil so they get recreated)
199198
sa, err := c.ensureSA(source)
199+
// recreate the pod if no existing pod is serving the latest image or correct spec
200+
overwritePod := overwrite || len(c.currentPodsWithCorrectImageAndSpec(source, sa.GetName())) == 0
201+
200202
if err != nil && !k8serror.IsAlreadyExists(err) {
201203
return errors.Wrapf(err, "error ensuring service account: %s", source.GetName())
202204
}
@@ -421,10 +423,9 @@ func (c *GrpcRegistryReconciler) removePods(pods []*corev1.Pod, namespace string
421423
// CheckRegistryServer returns true if the given CatalogSource is considered healthy; false otherwise.
422424
func (c *GrpcRegistryReconciler) CheckRegistryServer(catalogSource *v1alpha1.CatalogSource) (healthy bool, err error) {
423425
source := grpcCatalogSourceDecorator{catalogSource}
424-
425426
// Check on registry resources
426427
// TODO: add gRPC health check
427-
if len(c.currentPodsWithCorrectImage(source)) < 1 ||
428+
if len(c.currentPodsWithCorrectImageAndSpec(source, source.ServiceAccount().GetName())) < 1 ||
428429
c.currentService(source) == nil {
429430
healthy = false
430431
return
@@ -478,3 +479,30 @@ func (c *GrpcRegistryReconciler) podFailed(pod *corev1.Pod) (bool, error) {
478479
}
479480
return false, nil
480481
}
482+
483+
// podHashMatch will check the hash info in existing pod to ensure its
484+
// hash info matches the desired Service's hash.
485+
func podHashMatch(existing, new *corev1.Pod) bool {
486+
labels := existing.GetLabels()
487+
newLabels := new.GetLabels()
488+
// If both new & existing pods don't have labels, consider it not matched
489+
if len(labels) == 0 || len(newLabels) == 0 {
490+
return false
491+
}
492+
493+
existingPodSpecHash, ok := labels[PodHashLabelKey]
494+
if !ok {
495+
return false
496+
}
497+
498+
newPodSpecHash, ok := newLabels[PodHashLabelKey]
499+
if !ok {
500+
return false
501+
}
502+
503+
if existingPodSpecHash != newPodSpecHash {
504+
return false
505+
}
506+
507+
return true
508+
}

pkg/controller/registry/reconciler/grpc_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,74 @@ func TestGrpcRegistryReconciler(t *testing.T) {
339339
}
340340
}
341341

342+
func TestRegistryPodPriorityClass(t *testing.T) {
343+
now := func() metav1.Time { return metav1.Date(2018, time.January, 26, 20, 40, 0, 0, time.UTC) }
344+
345+
type cluster struct {
346+
k8sObjs []runtime.Object
347+
}
348+
type in struct {
349+
cluster cluster
350+
catsrc *v1alpha1.CatalogSource
351+
}
352+
tests := []struct {
353+
testName string
354+
in in
355+
priorityclass string
356+
}{
357+
{
358+
testName: "Grpc/WithValidPriorityClassAnnotation",
359+
in: in{
360+
catsrc: grpcCatalogSourceWithAnnotations(map[string]string{
361+
"operatorframework.io/priorityclass": "system-cluster-critical",
362+
}),
363+
},
364+
priorityclass: "system-cluster-critical",
365+
},
366+
{
367+
testName: "Grpc/WithInvalidPriorityClassAnnotation",
368+
in: in{
369+
catsrc: grpcCatalogSourceWithAnnotations(map[string]string{
370+
"operatorframework.io/priorityclass": "",
371+
}),
372+
},
373+
priorityclass: "",
374+
},
375+
{
376+
testName: "Grpc/WithNoPriorityClassAnnotation",
377+
in: in{
378+
catsrc: grpcCatalogSourceWithAnnotations(map[string]string{
379+
"annotationkey": "annotationvalue",
380+
}),
381+
},
382+
priorityclass: "",
383+
},
384+
}
385+
for _, tt := range tests {
386+
t.Run(tt.testName, func(t *testing.T) {
387+
stopc := make(chan struct{})
388+
defer close(stopc)
389+
390+
factory, client := fakeReconcilerFactory(t, stopc, withNow(now), withK8sObjs(tt.in.cluster.k8sObjs...), withK8sClientOptions(clientfake.WithNameGeneration(t)))
391+
rec := factory.ReconcilerForSource(tt.in.catsrc)
392+
393+
err := rec.EnsureRegistryServer(tt.in.catsrc)
394+
require.NoError(t, err)
395+
396+
// Check for resource existence
397+
decorated := grpcCatalogSourceDecorator{tt.in.catsrc}
398+
pod := decorated.Pod(tt.in.catsrc.GetName())
399+
listOptions := metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{CatalogSourceLabelKey: tt.in.catsrc.GetName()}).String()}
400+
outPods, podErr := client.KubernetesInterface().CoreV1().Pods(pod.GetNamespace()).List(context.TODO(), listOptions)
401+
require.NoError(t, podErr)
402+
require.Len(t, outPods.Items, 1)
403+
outPod := outPods.Items[0]
404+
require.Equal(t, tt.priorityclass, outPod.Spec.PriorityClassName)
405+
require.Equal(t, pod.GetLabels()[PodHashLabelKey], outPod.GetLabels()[PodHashLabelKey])
406+
})
407+
}
408+
}
409+
342410
func TestGrpcRegistryChecker(t *testing.T) {
343411
type cluster struct {
344412
k8sObjs []runtime.Object

pkg/controller/registry/reconciler/reconciler.go

+28
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
package reconciler
33

44
import (
5+
"fmt"
6+
"hash/fnv"
57
"strings"
68

79
v1 "k8s.io/api/core/v1"
810
"k8s.io/apimachinery/pkg/api/resource"
911
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/util/rand"
1013

1114
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1215
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
16+
hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash"
1317
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
1418
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
1519
)
@@ -19,6 +23,10 @@ type nowFunc func() metav1.Time
1923
const (
2024
// CatalogSourceLabelKey is the key for a label containing a CatalogSource name.
2125
CatalogSourceLabelKey string = "olm.catalogSource"
26+
// CatalogPriorityClassKey is the key of an annotation in default catalogsources
27+
CatalogPriorityClassKey string = "operatorframework.io/priorityclass"
28+
// PodHashLabelKey is the key of a label for podspec hash information
29+
PodHashLabelKey = "olm.pod-spec-hash"
2230
)
2331

2432
// RegistryEnsurer describes methods for ensuring a registry exists.
@@ -160,5 +168,25 @@ func Pod(source *v1alpha1.CatalogSource, name string, image string, saName strin
160168
ServiceAccountName: saName,
161169
},
162170
}
171+
172+
// Set priorityclass if its annotation exists
173+
if prio, ok := annotations[CatalogPriorityClassKey]; ok && prio != "" {
174+
pod.Spec.PriorityClassName = prio
175+
}
176+
177+
// Add PodSpec hash
178+
// This hash info will be used to detect PodSpec changes
179+
if labels == nil {
180+
labels = make(map[string]string)
181+
}
182+
labels[PodHashLabelKey] = hashPodSpec(pod.Spec)
183+
pod.SetLabels(labels)
163184
return pod
164185
}
186+
187+
// hashPodSpec calculates a hash given a copy of the pod spec
188+
func hashPodSpec(spec v1.PodSpec) string {
189+
hasher := fnv.New32a()
190+
hashutil.DeepHashObject(hasher, &spec)
191+
return rand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
192+
}

0 commit comments

Comments
 (0)