Skip to content

Commit c9ccfd9

Browse files
committed
Add PriorityClass setting to registry pods for default CatalogSource
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 5c40752 commit c9ccfd9

File tree

4 files changed

+132
-7
lines changed

4 files changed

+132
-7
lines changed

Diff for: 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
}

Diff for: 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+
}

Diff for: 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

Diff for: 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)