Skip to content

Commit 97abcdd

Browse files
committed
Svcwatcher adapted to be able to recognize all 3 network management APIs indepedently from each other
1 parent 4764e3d commit 97abcdd

File tree

5 files changed

+77
-32
lines changed

5 files changed

+77
-32
lines changed

integration/manifests/svcwatcher/0svcwatcher_rbac.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ rules:
3636
- apiGroups:
3737
- "danm.k8s.io"
3838
resources:
39-
- danmnets
4039
- danmeps
4140
verbs:
4241
- get

pkg/netcontrol/netcontrol.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ func PutNetwork(danmClient danmclientset.Interface, dnet *danmtypes.DanmNet) (bo
266266
cn := ConvertDnetToCnet(dnet)
267267
_, err = danmClient.DanmV1().ClusterNetworks().Update(cn)
268268
} else {
269-
return wasResourceAlreadyUpdated, errors.New("can't refresh network object because it has an invalid type:" + dnet.TypeMeta.Kind)
270-
}
269+
return wasResourceAlreadyUpdated, errors.New("can't refresh network object because it has an invalid type:" + dnet.TypeMeta.Kind)
270+
}
271271
if err != nil {
272272
if strings.Contains(err.Error(),datastructs.OptimisticLockErrorMsg) {
273273
wasResourceAlreadyUpdated = true

pkg/svccontrol/controller.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,12 @@ func (c *Controller) delDanmep(obj interface{}) {
410410
}
411411
epNew := ep.DeepCopy()
412412
annotations := epNew.GetAnnotations()
413-
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
413+
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
414414
if err != nil {
415415
glog.Errorf("delDanmEp: selector %s", err)
416416
return
417417
}
418-
if len(selectorMap) == 0 || svcNet != de.Spec.NetworkName || epNew.Namespace != deNs {
418+
if len(selectorMap) == 0 || !isDepSelectedBySvc(de, svcNets) || epNew.Namespace != deNs {
419419
continue
420420
}
421421
deMap := de.GetLabels()
@@ -482,15 +482,15 @@ func (c *Controller) updatePod(old, new interface{}) {
482482
}
483483
// first we need to reflect status change
484484
if oldReady != newReady {
485-
// status change
485+
// status change
486486
epList := c.UpdatePodStatusInEps(epsList, newPod, oldReady, newReady)
487487
if len(epList) > 0 {
488488
c.UpdateEndpointsList(epList)
489489
}
490490
}
491491
// label change has lower priority
492492
if labelChange {
493-
// label change
493+
// label change
494494
podName := newPod.Name
495495
podNs := newPod.Namespace
496496
desList, err := c.danmepLister.List(sel)
@@ -528,19 +528,19 @@ func (c *Controller) addSvc(obj interface{}) {
528528
svcNs := svc.Namespace
529529
svcName := svc.Name
530530
annotations := svc.Annotations
531-
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
531+
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
532532
if err != nil {
533533
glog.Errorf("addSvc: get anno %s", err)
534534
return
535535
}
536-
if len(selectorMap) > 0 && svcNet != "" {
536+
if len(selectorMap) > 0 && len(svcNets) > 0 {
537537
sel := labels.Everything()
538538
d, err := c.danmepLister.List(sel)
539539
if err != nil {
540540
glog.Errorf("addSvc: get danmep %s", err)
541541
return
542542
}
543-
deList := SelectDesMatchLabels(d, selectorMap, svcNet, svcNs)
543+
deList := SelectDesMatchLabels(d, selectorMap, svcNets, svcNs)
544544
e, err := c.epsLister.List(sel)
545545
if err != nil {
546546
glog.Errorf("addSvc: get eps %s", err)

pkg/svccontrol/utils.go

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@ import (
55
"github.com/golang/glog"
66
corev1 "k8s.io/api/core/v1"
77
danmv1 "github.com/nokia/danm/crd/apis/danm/v1"
8+
"github.com/nokia/danm/pkg/netcontrol"
89
"reflect"
910
)
1011

11-
const danmSelector = "danm.k8s.io/selector"
12-
const danmNetwork = "danm.k8s.io/network"
13-
const TolerateUnreadyEps = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
12+
const (
13+
PodSelector = "danm.k8s.io/selector"
14+
DanmNetSelector = "danm.k8s.io/network"
15+
TenantNetSelector = "danm.k8s.io/tenantNetwork"
16+
ClusterNetSelector = "danm.k8s.io/clusterNetwork"
17+
TolerateUnreadyEps = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
18+
)
19+
1420

1521
func IsContain(ep, svc map[string]string) bool {
1622
epFit := true
@@ -28,26 +34,35 @@ func IsContain(ep, svc map[string]string) bool {
2834
return epFit
2935
}
3036

31-
func GetDanmSvcAnnotations(annotations map[string]string) (map[string]string, string, error) {
37+
func GetDanmSvcAnnotations(annotations map[string]string) (map[string]string, map[string]string, error) {
3238
selectorMap := make(map[string]string)
33-
svcNet := ""
34-
if danmSel, ok := annotations[danmSelector]; ok {
39+
netSelectors := make(map[string]string)
40+
if danmSel, ok := annotations[PodSelector]; ok {
3541
if danmSel != "" {
3642
err := json.Unmarshal([]byte(danmSel), &selectorMap)
3743
if err != nil {
3844
glog.Errorf("utils: json error: %s", err)
39-
return selectorMap, svcNet, err
45+
return selectorMap, netSelectors, err
4046
}
4147
}
4248
}
43-
44-
if danmNet, ok := annotations[danmNetwork]; ok {
49+
//TODO: instead of this we might need to iterate over the whole annotation and do strings.EqualFold for a case-insensitive key comparison
50+
if danmNet, ok := annotations[DanmNetSelector]; ok {
4551
if danmNet != "" {
46-
svcNet = danmNet
52+
netSelectors[netcontrol.DanmNetKind] = danmNet
4753
}
4854
}
49-
50-
return selectorMap, svcNet, nil
55+
if tenantNet, ok := annotations[TenantNetSelector]; ok {
56+
if tenantNet != "" {
57+
netSelectors[netcontrol.TenantNetworkKind] = tenantNet
58+
}
59+
}
60+
if clusterNet, ok := annotations[ClusterNetSelector]; ok {
61+
if clusterNet != "" {
62+
netSelectors[netcontrol.ClusterNetworkKind] = clusterNet
63+
}
64+
}
65+
return selectorMap, netSelectors, nil
5166
}
5267

5368
func PodReady(pod *corev1.Pod) bool {
@@ -59,7 +74,7 @@ func PodReady(pod *corev1.Pod) bool {
5974
return false
6075
}
6176

62-
func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, svcNet string, svcNs string) []*danmv1.DanmEp {
77+
func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, svcNets map[string]string, svcNs string) []*danmv1.DanmEp {
6378
var deList []*danmv1.DanmEp
6479
for _, de := range des {
6580
deFit := true
@@ -68,7 +83,7 @@ func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, s
6883
} else {
6984
deMap := de.GetLabels()
7085
deFit = IsContain(deMap, selectorMap)
71-
if deFit && de.Spec.NetworkName != svcNet {
86+
if deFit && !isDepSelectedBySvc(de, svcNets) {
7287
deFit = false
7388
}
7489
}
@@ -95,12 +110,12 @@ func SvcChanged(oldSvc, newSvc *corev1.Service) bool {
95110
// danm svc annotations and annotations.ealy change are relevant
96111
oldAnno := oldSvc.Annotations
97112
newAnno := newSvc.Annotations
98-
oldSelMap, oldNet, oldErr := GetDanmSvcAnnotations(oldAnno)
99-
newSelMap, newNet, newErr := GetDanmSvcAnnotations(newAnno)
113+
oldSelMap, oldNets, oldErr := GetDanmSvcAnnotations(oldAnno)
114+
newSelMap, newNets, newErr := GetDanmSvcAnnotations(newAnno)
100115
if oldErr != nil || newErr != nil {
101116
return true
102117
}
103-
if reflect.DeepEqual(oldSelMap, newSelMap) && oldNet == newNet && reflect.DeepEqual(oldSvc.Spec.Ports, newSvc.Spec.Ports) && (oldSvc.Annotations[TolerateUnreadyEps] == newSvc.Annotations[TolerateUnreadyEps]) {
118+
if reflect.DeepEqual(oldSelMap, newSelMap) && reflect.DeepEqual(oldNets, newNets) && reflect.DeepEqual(oldSvc.Spec.Ports, newSvc.Spec.Ports) && (oldSvc.Annotations[TolerateUnreadyEps] == newSvc.Annotations[TolerateUnreadyEps]) {
104119
// no change
105120
return false
106121
}
@@ -120,11 +135,11 @@ func MatchExistingSvc(de *danmv1.DanmEp, servicesList []*corev1.Service) []*core
120135
var svcList []*corev1.Service
121136
for _, svc := range servicesList {
122137
annotations := svc.GetAnnotations()
123-
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
138+
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
124139
if err != nil {
125140
return svcList
126141
}
127-
if len(selectorMap) == 0 || svcNet != de.Spec.NetworkName || svc.GetNamespace() != deNs {
142+
if len(selectorMap) == 0 || !isDepSelectedBySvc(de, svcNets) || svc.GetNamespace() != deNs {
128143
continue
129144
}
130145
deMap := de.GetLabels()
@@ -137,3 +152,24 @@ func MatchExistingSvc(de *danmv1.DanmEp, servicesList []*corev1.Service) []*core
137152
return svcList
138153
}
139154

155+
func isDepSelectedBySvc(dep *danmv1.DanmEp, netSelectors map[string]string) bool {
156+
if len(netSelectors) == 0 {
157+
return false
158+
}
159+
if danmNet, ok := netSelectors[netcontrol.DanmNetKind]; ok {
160+
if danmNet == dep.Spec.NetworkName && (netcontrol.DanmNetKind == dep.Spec.ApiType || "" == dep.Spec.ApiType) {
161+
return true
162+
}
163+
}
164+
if tenantNet, ok := netSelectors[netcontrol.TenantNetworkKind]; ok {
165+
if tenantNet == dep.Spec.NetworkName && netcontrol.TenantNetworkKind == dep.Spec.ApiType {
166+
return true
167+
}
168+
}
169+
if clusterNet, ok := netSelectors[netcontrol.ClusterNetworkKind]; ok {
170+
if clusterNet == dep.Spec.NetworkName && netcontrol.ClusterNetworkKind == dep.Spec.ApiType {
171+
return true
172+
}
173+
}
174+
return false
175+
}

schema/DanmService.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,21 @@ metadata:
1818
# DANM uses the information for the same purpose as teh default Service controller - for every Pod matching all key-value pairs in the selector field, one Endpoint will be created.
1919
# MANDATORY - JSON FORMATTED LIST OF STRING:STRING ASSOCIATIONS (e.g. '{"app":"loadbalancer"},{"type":"sctp"}')
2020
danm.k8s.io/selector: ## POD_SELECTORS ##
21-
# When DANM creates an Endpoint for a selected Pod, it will populate it with the selected interface's IP.
22-
# The selected interface will be the one which is connected to the DanmNet object identified (i.e. matching ObjectMeta.Name) in this attribute.
21+
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
22+
# If you want a Service to select an interface connected to a DanmNet, set the name of the DanmNet object into this attribute.
2323
# Pods, DanmNets, and Services are all namespaced resources, so an Endpoint is created only if all three are within the same K8s namespace.
24-
# MANDATORY - STRING
24+
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
2525
danm.k8s.io/network: ## NETWORK_SELECTOR ##
26+
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
27+
# If you want a Service to select an interface connected to a TenantNetwork, set the name of the TenantNetwork object into this attribute.
28+
# Pods, TenantNetworks, and Services are all namespaced resources, so an Endpoint is created only if all three are within the same K8s namespace.
29+
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
30+
danm.k8s.io/tenantNetwork: ## NETWORK_SELECTOR ##
31+
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
32+
# If you want a Service to select an interface connected to a ClusterNetwork, set the name of the ClusterNetwork object into this attribute.
33+
# As ClusterNetworks are not namespaced resources, EndPoints are created whenever a Pod connects to a matching ClusterNetwork in the same namespace as this Service.
34+
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
35+
danm.k8s.io/clusterNetwork: ## NETWORK_SELECTOR ##
2636
spec:
2737
# DANM recognized Services are selectorless Services, because we want to avoid default Kubernetes controllers to create an Endpoint to a wrong network interface.
2838
# Selectorless Services don't have a spec.selector present in their object.

0 commit comments

Comments
 (0)