Skip to content

Commit 7fb8615

Browse files
authored
add labelSelector support; set listener Service label to name; unit tests (#2352)
Fixes #2109
1 parent 82064f6 commit 7fb8615

6 files changed

Lines changed: 145 additions & 26 deletions

File tree

internal/kube/controller/controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ func (c *Controller) SetAnnotations(namespace string, name string, kind string,
162162
return c.labelling.SetAnnotations(namespace, name, kind, annotations)
163163
}
164164

165+
func (c *Controller) SetObjectMetadata(namespace string, name string, kind string, meta *metav1.ObjectMeta) bool {
166+
return c.labelling.SetObjectMetadata(namespace, name, kind, meta)
167+
}
168+
165169
func (c *Controller) Namespace() string {
166170
return c.self.Namespace
167171
}

internal/kube/qdr/update_config.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
type Labelling interface {
1414
SetLabels(namespace string, name string, kind string, labels map[string]string) bool
1515
SetAnnotations(namespace string, name string, kind string, annotations map[string]string) bool
16+
SetObjectMetadata(namespace string, name string, kind string, meta *metav1.ObjectMeta) bool
1617
}
1718

1819
func UpdateRouterConfig(client kubernetes.Interface, name string, namespace string, ctxt context.Context, update qdr.ConfigUpdate, labelling Labelling) error {
@@ -43,10 +44,7 @@ func updateRouterConfig(client kubernetes.Interface, name string, namespace stri
4344
updated = true
4445
}
4546
if labelling != nil {
46-
if labelling.SetLabels(namespace, name, "ConfigMap", current.ObjectMeta.Labels) {
47-
updated = true
48-
}
49-
if labelling.SetAnnotations(namespace, name, "ConfigMap", current.ObjectMeta.Annotations) {
47+
if labelling.SetObjectMetadata(namespace, name, "ConfigMap", &current.ObjectMeta) {
5048
updated = true
5149
}
5250
}

internal/kube/site/labels/registry.go

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"strings"
66

77
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
k8slabels "k8s.io/apimachinery/pkg/labels"
810
"k8s.io/client-go/tools/cache"
911
)
1012

@@ -42,11 +44,11 @@ func (l *LabelsAndAnnotations) Update(key string, cm *corev1.ConfigMap) error {
4244
func (l *LabelsAndAnnotations) SetLabels(namespace string, name string, kind string, labels map[string]string) bool {
4345
desired := map[string]string{}
4446
if registry, ok := l.namespaces[namespace]; ok {
45-
registry.setLabels(name, kind, desired)
47+
registry.setLabels(name, kind, labels, desired)
4648
}
4749
if namespace != l.controllerNamespace {
4850
if registry, ok := l.namespaces[l.controllerNamespace]; ok {
49-
registry.setLabels(name, kind, desired)
51+
registry.setLabels(name, kind, labels, desired)
5052
}
5153
}
5254
return setValues(desired, labels)
@@ -55,24 +57,56 @@ func (l *LabelsAndAnnotations) SetLabels(namespace string, name string, kind str
5557
func (l *LabelsAndAnnotations) SetAnnotations(namespace string, name string, kind string, annotations map[string]string) bool {
5658
desired := map[string]string{}
5759
if registry, ok := l.namespaces[namespace]; ok {
58-
registry.setAnnotations(name, kind, desired)
60+
registry.setAnnotations(name, kind, annotations, desired)
5961
}
6062
if namespace != l.controllerNamespace {
6163
if registry, ok := l.namespaces[l.controllerNamespace]; ok {
62-
registry.setAnnotations(name, kind, desired)
64+
registry.setAnnotations(name, kind, annotations, desired)
6365
}
6466
}
6567
return setValues(desired, annotations)
6668
}
6769

70+
func (l *LabelsAndAnnotations) SetObjectMetadata(namespace string, name string, kind string, meta *metav1.ObjectMeta) bool {
71+
if meta == nil {
72+
return false
73+
}
74+
if meta.Labels == nil {
75+
meta.Labels = map[string]string{}
76+
}
77+
if meta.Annotations == nil {
78+
meta.Annotations = map[string]string{}
79+
}
80+
changed := false
81+
if registry, ok := l.namespaces[namespace]; ok {
82+
if registry.filter(name, kind, meta.Labels, meta.Labels, meta.Annotations) {
83+
changed = true
84+
}
85+
}
86+
if namespace != l.controllerNamespace {
87+
if registry, ok := l.namespaces[l.controllerNamespace]; ok {
88+
if registry.filter(name, kind, meta.Labels, meta.Labels, meta.Annotations) {
89+
changed = true
90+
}
91+
}
92+
}
93+
return changed
94+
}
95+
6896
type Registry struct {
69-
config map[string]*corev1.ConfigMap
97+
config map[string]*templateEntry
7098
log *slog.Logger
7199
}
72100

101+
type templateEntry struct {
102+
cm *corev1.ConfigMap
103+
selector k8slabels.Selector
104+
invalid bool
105+
}
106+
73107
func newRegistry(log *slog.Logger) *Registry {
74108
return &Registry{
75-
config: map[string]*corev1.ConfigMap{},
109+
config: map[string]*templateEntry{},
76110
log: log,
77111
}
78112
}
@@ -94,27 +128,53 @@ func (r *Registry) update(key string, cm *corev1.ConfigMap) error {
94128
slog.String("name", cm.Name),
95129
)
96130
}
97-
r.config[key] = cm
131+
entry := &templateEntry{cm: cm}
132+
if cm.Data != nil {
133+
if selector, ok := cm.Data["labelSelector"]; ok && selector != "" {
134+
req, err := k8slabels.Parse(selector)
135+
if err != nil {
136+
r.log.Info("Ignoring label-template due to invalid labelSelector",
137+
slog.String("name", cm.Name),
138+
slog.String("namespace", cm.Namespace),
139+
slog.String("labelSelector", selector),
140+
slog.Any("error", err),
141+
)
142+
entry.invalid = true
143+
} else {
144+
entry.selector = req
145+
}
146+
}
147+
}
148+
r.config[key] = entry
98149
return nil
99150
}
100151

101-
func (r *Registry) setLabels(name string, kind string, labels map[string]string) bool {
102-
return r.filter(name, kind, labels, nil)
152+
func (r *Registry) setLabels(name string, kind string, target map[string]string, labels map[string]string) bool {
153+
return r.filter(name, kind, target, labels, nil)
103154
}
104155

105-
func (r *Registry) setAnnotations(name string, kind string, annotations map[string]string) bool {
106-
return r.filter(name, kind, nil, annotations)
156+
func (r *Registry) setAnnotations(name string, kind string, target map[string]string, annotations map[string]string) bool {
157+
return r.filter(name, kind, target, nil, annotations)
107158
}
108159

109-
func (r *Registry) filter(name string, kind string, labels map[string]string, annotations map[string]string) bool {
160+
func (r *Registry) filter(name string, kind string, target map[string]string, labels map[string]string, annotations map[string]string) bool {
110161
changed := false
111-
for _, cm := range r.config {
162+
for _, entry := range r.config {
163+
if entry.invalid {
164+
continue
165+
}
166+
cm := entry.cm
112167
if !matchKey(cm, "name", name) {
113168
continue
114169
}
115170
if !matchKey(cm, "kind", kind) {
116171
continue
117172
}
173+
if entry.selector != nil {
174+
if target == nil || !entry.selector.Matches(k8slabels.Set(target)) {
175+
continue
176+
}
177+
}
118178
excludes := exclude(cm)
119179
if labels != nil {
120180
for k, v := range cm.ObjectMeta.Labels {

internal/kube/site/labels/registry_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,41 @@ func configmap(key string, data map[string]string, labels map[string]string, ann
351351
},
352352
}
353353
}
354+
355+
func updateWithSelector(key string, labels map[string]string, annotations map[string]string, selector string) Update {
356+
data := map[string]string{}
357+
if selector != "" {
358+
data["labelSelector"] = selector
359+
}
360+
if labels == nil {
361+
labels = map[string]string{}
362+
}
363+
labels["skupper.io/label-template"] = "true"
364+
if len(data) == 0 {
365+
data = nil
366+
}
367+
return configmap(key, data, labels, annotations)
368+
}
369+
370+
func TestLabelSelectorFiltering(t *testing.T) {
371+
registry := NewLabelsAndAnnotations("default")
372+
// apply a configmap with a selector that matches listener=my-listener
373+
registry.Update("test/selector", updateWithSelector("test/selector", map[string]string{"acme.com/foo": "bar"}, nil, "internal.skupper.io/listener in (my-listener)").config)
374+
// Case 1: labels match selector
375+
actual := map[string]string{"internal.skupper.io/listener": "my-listener"}
376+
changed := registry.SetLabels("test", "svc-a", "Service", actual)
377+
assert.Assert(t, changed)
378+
assert.Equal(t, actual["acme.com/foo"], "bar")
379+
// Case 2: labels do not match selector
380+
actual2 := map[string]string{"internal.skupper.io/listener": "other"}
381+
changed2 := registry.SetLabels("test", "svc-b", "Service", actual2)
382+
assert.Assert(t, !changed2)
383+
_, present := actual2["acme.com/foo"]
384+
assert.Assert(t, !present)
385+
// Case 3: invalid selector should be ignored (no application)
386+
registry.Update("test/invalid", updateWithSelector("test/invalid", map[string]string{"acme.com/bar": "baz"}, nil, "this is not a valid selector").config)
387+
actual3 := map[string]string{"internal.skupper.io/listener": "my-listener"}
388+
_ = registry.SetLabels("test", "svc-c", "Service", actual3)
389+
_, present = actual3["acme.com/bar"]
390+
assert.Assert(t, !present)
391+
}

internal/kube/site/resources/apply.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"strconv"
99

10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011
"k8s.io/apimachinery/pkg/runtime/schema"
1112
"sigs.k8s.io/yaml"
1213

@@ -27,6 +28,7 @@ var routerLocalServiceTemplate string
2728
type Labelling interface {
2829
SetLabels(namespace string, name string, kind string, labels map[string]string) bool
2930
SetAnnotations(namespace string, name string, kind string, annotations map[string]string) bool
31+
SetObjectMetadata(namespace string, name string, kind string, meta *metav1.ObjectMeta) bool
3032
}
3133

3234
func resourceTemplates(site *skupperv2alpha1.Site, group string, size sizing.Sizing, labelling Labelling) []resource.Template {
@@ -76,8 +78,11 @@ func (p *CoreParams) setLabelsAndAnnotations(labelling Labelling, namespace stri
7678
}
7779
p.Labels = map[string]string{}
7880
p.Annotations = map[string]string{}
79-
labelling.SetLabels(namespace, name, kind, p.Labels)
80-
labelling.SetAnnotations(namespace, name, kind, p.Annotations)
81+
meta := &metav1.ObjectMeta{
82+
Labels: p.Labels,
83+
Annotations: p.Annotations,
84+
}
85+
labelling.SetObjectMetadata(namespace, name, kind, meta)
8186
quoteValues(p.Labels)
8287
quoteValues(p.Annotations)
8388
return p

internal/kube/site/site.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type SecuredAccessFactory interface {
3939
type Labelling interface {
4040
SetLabels(namespace string, name string, kind string, labels map[string]string) bool
4141
SetAnnotations(namespace string, name string, kind string, annotations map[string]string) bool
42+
SetObjectMetadata(namespace string, name string, kind string, meta *metav1.ObjectMeta) bool
4243
}
4344

4445
type Site struct {
@@ -526,6 +527,11 @@ func (s *Site) Expose(exposed *ExposedPortSet) error {
526527
ctxt := context.TODO()
527528
current, err := s.clients.GetKubeClient().CoreV1().Services(s.namespace).Get(ctxt, exposed.Host, metav1.GetOptions{})
528529
if errors.IsNotFound(err) {
530+
listenerName := ""
531+
for name := range exposed.Ports {
532+
listenerName = name
533+
break
534+
}
529535
service := &corev1.Service{
530536
TypeMeta: metav1.TypeMeta{
531537
APIVersion: "v1",
@@ -534,7 +540,7 @@ func (s *Site) Expose(exposed *ExposedPortSet) error {
534540
ObjectMeta: metav1.ObjectMeta{
535541
Name: exposed.Host,
536542
Labels: map[string]string{
537-
"internal.skupper.io/listener": "true",
543+
"internal.skupper.io/listener": listenerName,
538544
},
539545
Annotations: map[string]string{
540546
"internal.skupper.io/controlled": "true",
@@ -546,8 +552,7 @@ func (s *Site) Expose(exposed *ExposedPortSet) error {
546552
},
547553
}
548554
if s.labelling != nil {
549-
s.labelling.SetLabels(s.namespace, service.Name, "Service", service.ObjectMeta.Labels)
550-
s.labelling.SetAnnotations(s.namespace, service.Name, "Service", service.ObjectMeta.Annotations)
555+
s.labelling.SetObjectMetadata(s.namespace, service.Name, "Service", &service.ObjectMeta)
551556
}
552557
updatePorts(&service.Spec, exposed.Ports)
553558
if len(service.Spec.Ports) == 0 {
@@ -583,17 +588,26 @@ func (s *Site) Expose(exposed *ExposedPortSet) error {
583588
if updatePorts(&current.Spec, exposed.Ports) {
584589
updated = true
585590
}
591+
if current.ObjectMeta.Labels == nil {
592+
current.ObjectMeta.Labels = map[string]string{}
593+
}
594+
newListenerName := ""
595+
for name := range exposed.Ports {
596+
newListenerName = name
597+
break
598+
}
599+
if val, ok := current.ObjectMeta.Labels["internal.skupper.io/listener"]; !ok || val != newListenerName {
600+
current.ObjectMeta.Labels["internal.skupper.io/listener"] = newListenerName
601+
updated = true
602+
}
586603
if s.labelling != nil {
587604
if current.ObjectMeta.Labels == nil {
588605
current.ObjectMeta.Labels = map[string]string{}
589606
}
590607
if current.ObjectMeta.Annotations == nil {
591608
current.ObjectMeta.Annotations = map[string]string{}
592609
}
593-
if s.labelling.SetLabels(s.namespace, current.Name, "Service", current.ObjectMeta.Labels) {
594-
updated = true
595-
}
596-
if s.labelling.SetAnnotations(s.namespace, current.Name, "Service", current.ObjectMeta.Annotations) {
610+
if s.labelling.SetObjectMetadata(s.namespace, current.Name, "Service", &current.ObjectMeta) {
597611
updated = true
598612
}
599613
}

0 commit comments

Comments
 (0)