Skip to content

Commit

Permalink
Svcwatcher adapted to be able to recognize all 3 network management A…
Browse files Browse the repository at this point in the history
…PIs indepedently from each other
  • Loading branch information
Levovar committed Jun 27, 2019
1 parent 4764e3d commit 97abcdd
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 32 deletions.
1 change: 0 additions & 1 deletion integration/manifests/svcwatcher/0svcwatcher_rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ rules:
- apiGroups:
- "danm.k8s.io"
resources:
- danmnets
- danmeps
verbs:
- get
Expand Down
4 changes: 2 additions & 2 deletions pkg/netcontrol/netcontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ func PutNetwork(danmClient danmclientset.Interface, dnet *danmtypes.DanmNet) (bo
cn := ConvertDnetToCnet(dnet)
_, err = danmClient.DanmV1().ClusterNetworks().Update(cn)
} else {
return wasResourceAlreadyUpdated, errors.New("can't refresh network object because it has an invalid type:" + dnet.TypeMeta.Kind)
}
return wasResourceAlreadyUpdated, errors.New("can't refresh network object because it has an invalid type:" + dnet.TypeMeta.Kind)
}
if err != nil {
if strings.Contains(err.Error(),datastructs.OptimisticLockErrorMsg) {
wasResourceAlreadyUpdated = true
Expand Down
14 changes: 7 additions & 7 deletions pkg/svccontrol/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,12 @@ func (c *Controller) delDanmep(obj interface{}) {
}
epNew := ep.DeepCopy()
annotations := epNew.GetAnnotations()
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
if err != nil {
glog.Errorf("delDanmEp: selector %s", err)
return
}
if len(selectorMap) == 0 || svcNet != de.Spec.NetworkName || epNew.Namespace != deNs {
if len(selectorMap) == 0 || !isDepSelectedBySvc(de, svcNets) || epNew.Namespace != deNs {
continue
}
deMap := de.GetLabels()
Expand Down Expand Up @@ -482,15 +482,15 @@ func (c *Controller) updatePod(old, new interface{}) {
}
// first we need to reflect status change
if oldReady != newReady {
// status change
// status change
epList := c.UpdatePodStatusInEps(epsList, newPod, oldReady, newReady)
if len(epList) > 0 {
c.UpdateEndpointsList(epList)
}
}
// label change has lower priority
if labelChange {
// label change
// label change
podName := newPod.Name
podNs := newPod.Namespace
desList, err := c.danmepLister.List(sel)
Expand Down Expand Up @@ -528,19 +528,19 @@ func (c *Controller) addSvc(obj interface{}) {
svcNs := svc.Namespace
svcName := svc.Name
annotations := svc.Annotations
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
if err != nil {
glog.Errorf("addSvc: get anno %s", err)
return
}
if len(selectorMap) > 0 && svcNet != "" {
if len(selectorMap) > 0 && len(svcNets) > 0 {
sel := labels.Everything()
d, err := c.danmepLister.List(sel)
if err != nil {
glog.Errorf("addSvc: get danmep %s", err)
return
}
deList := SelectDesMatchLabels(d, selectorMap, svcNet, svcNs)
deList := SelectDesMatchLabels(d, selectorMap, svcNets, svcNs)
e, err := c.epsLister.List(sel)
if err != nil {
glog.Errorf("addSvc: get eps %s", err)
Expand Down
74 changes: 55 additions & 19 deletions pkg/svccontrol/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import (
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
danmv1 "github.com/nokia/danm/crd/apis/danm/v1"
"github.com/nokia/danm/pkg/netcontrol"
"reflect"
)

const danmSelector = "danm.k8s.io/selector"
const danmNetwork = "danm.k8s.io/network"
const TolerateUnreadyEps = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
const (
PodSelector = "danm.k8s.io/selector"
DanmNetSelector = "danm.k8s.io/network"
TenantNetSelector = "danm.k8s.io/tenantNetwork"
ClusterNetSelector = "danm.k8s.io/clusterNetwork"
TolerateUnreadyEps = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
)


func IsContain(ep, svc map[string]string) bool {
epFit := true
Expand All @@ -28,26 +34,35 @@ func IsContain(ep, svc map[string]string) bool {
return epFit
}

func GetDanmSvcAnnotations(annotations map[string]string) (map[string]string, string, error) {
func GetDanmSvcAnnotations(annotations map[string]string) (map[string]string, map[string]string, error) {
selectorMap := make(map[string]string)
svcNet := ""
if danmSel, ok := annotations[danmSelector]; ok {
netSelectors := make(map[string]string)
if danmSel, ok := annotations[PodSelector]; ok {
if danmSel != "" {
err := json.Unmarshal([]byte(danmSel), &selectorMap)
if err != nil {
glog.Errorf("utils: json error: %s", err)
return selectorMap, svcNet, err
return selectorMap, netSelectors, err
}
}
}

if danmNet, ok := annotations[danmNetwork]; ok {
//TODO: instead of this we might need to iterate over the whole annotation and do strings.EqualFold for a case-insensitive key comparison
if danmNet, ok := annotations[DanmNetSelector]; ok {
if danmNet != "" {
svcNet = danmNet
netSelectors[netcontrol.DanmNetKind] = danmNet
}
}

return selectorMap, svcNet, nil
if tenantNet, ok := annotations[TenantNetSelector]; ok {
if tenantNet != "" {
netSelectors[netcontrol.TenantNetworkKind] = tenantNet
}
}
if clusterNet, ok := annotations[ClusterNetSelector]; ok {
if clusterNet != "" {
netSelectors[netcontrol.ClusterNetworkKind] = clusterNet
}
}
return selectorMap, netSelectors, nil
}

func PodReady(pod *corev1.Pod) bool {
Expand All @@ -59,7 +74,7 @@ func PodReady(pod *corev1.Pod) bool {
return false
}

func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, svcNet string, svcNs string) []*danmv1.DanmEp {
func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, svcNets map[string]string, svcNs string) []*danmv1.DanmEp {
var deList []*danmv1.DanmEp
for _, de := range des {
deFit := true
Expand All @@ -68,7 +83,7 @@ func SelectDesMatchLabels(des []*danmv1.DanmEp, selectorMap map[string]string, s
} else {
deMap := de.GetLabels()
deFit = IsContain(deMap, selectorMap)
if deFit && de.Spec.NetworkName != svcNet {
if deFit && !isDepSelectedBySvc(de, svcNets) {
deFit = false
}
}
Expand All @@ -95,12 +110,12 @@ func SvcChanged(oldSvc, newSvc *corev1.Service) bool {
// danm svc annotations and annotations.ealy change are relevant
oldAnno := oldSvc.Annotations
newAnno := newSvc.Annotations
oldSelMap, oldNet, oldErr := GetDanmSvcAnnotations(oldAnno)
newSelMap, newNet, newErr := GetDanmSvcAnnotations(newAnno)
oldSelMap, oldNets, oldErr := GetDanmSvcAnnotations(oldAnno)
newSelMap, newNets, newErr := GetDanmSvcAnnotations(newAnno)
if oldErr != nil || newErr != nil {
return true
}
if reflect.DeepEqual(oldSelMap, newSelMap) && oldNet == newNet && reflect.DeepEqual(oldSvc.Spec.Ports, newSvc.Spec.Ports) && (oldSvc.Annotations[TolerateUnreadyEps] == newSvc.Annotations[TolerateUnreadyEps]) {
if reflect.DeepEqual(oldSelMap, newSelMap) && reflect.DeepEqual(oldNets, newNets) && reflect.DeepEqual(oldSvc.Spec.Ports, newSvc.Spec.Ports) && (oldSvc.Annotations[TolerateUnreadyEps] == newSvc.Annotations[TolerateUnreadyEps]) {
// no change
return false
}
Expand All @@ -120,11 +135,11 @@ func MatchExistingSvc(de *danmv1.DanmEp, servicesList []*corev1.Service) []*core
var svcList []*corev1.Service
for _, svc := range servicesList {
annotations := svc.GetAnnotations()
selectorMap, svcNet, err := GetDanmSvcAnnotations(annotations)
selectorMap, svcNets, err := GetDanmSvcAnnotations(annotations)
if err != nil {
return svcList
}
if len(selectorMap) == 0 || svcNet != de.Spec.NetworkName || svc.GetNamespace() != deNs {
if len(selectorMap) == 0 || !isDepSelectedBySvc(de, svcNets) || svc.GetNamespace() != deNs {
continue
}
deMap := de.GetLabels()
Expand All @@ -137,3 +152,24 @@ func MatchExistingSvc(de *danmv1.DanmEp, servicesList []*corev1.Service) []*core
return svcList
}

func isDepSelectedBySvc(dep *danmv1.DanmEp, netSelectors map[string]string) bool {
if len(netSelectors) == 0 {
return false
}
if danmNet, ok := netSelectors[netcontrol.DanmNetKind]; ok {
if danmNet == dep.Spec.NetworkName && (netcontrol.DanmNetKind == dep.Spec.ApiType || "" == dep.Spec.ApiType) {
return true
}
}
if tenantNet, ok := netSelectors[netcontrol.TenantNetworkKind]; ok {
if tenantNet == dep.Spec.NetworkName && netcontrol.TenantNetworkKind == dep.Spec.ApiType {
return true
}
}
if clusterNet, ok := netSelectors[netcontrol.ClusterNetworkKind]; ok {
if clusterNet == dep.Spec.NetworkName && netcontrol.ClusterNetworkKind == dep.Spec.ApiType {
return true
}
}
return false
}
16 changes: 13 additions & 3 deletions schema/DanmService.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@ metadata:
# 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.
# MANDATORY - JSON FORMATTED LIST OF STRING:STRING ASSOCIATIONS (e.g. '{"app":"loadbalancer"},{"type":"sctp"}')
danm.k8s.io/selector: ## POD_SELECTORS ##
# When DANM creates an Endpoint for a selected Pod, it will populate it with the selected interface's IP.
# The selected interface will be the one which is connected to the DanmNet object identified (i.e. matching ObjectMeta.Name) in this attribute.
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
# If you want a Service to select an interface connected to a DanmNet, set the name of the DanmNet object into this attribute.
# Pods, DanmNets, and Services are all namespaced resources, so an Endpoint is created only if all three are within the same K8s namespace.
# MANDATORY - STRING
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
danm.k8s.io/network: ## NETWORK_SELECTOR ##
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
# If you want a Service to select an interface connected to a TenantNetwork, set the name of the TenantNetwork object into this attribute.
# Pods, TenantNetworks, and Services are all namespaced resources, so an Endpoint is created only if all three are within the same K8s namespace.
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
danm.k8s.io/tenantNetwork: ## NETWORK_SELECTOR ##
# When DANM creates an Endpoint for a selected Pod, it populates it with the selected interface's IP.
# If you want a Service to select an interface connected to a ClusterNetwork, set the name of the ClusterNetwork object into this attribute.
# As ClusterNetworks are not namespaced resources, EndPoints are created whenever a Pod connects to a matching ClusterNetwork in the same namespace as this Service.
# OPTIONAL {AT LEAST ONE OF "network", "tenantNetwork", AND "clusterNetwork" shall be defined } - STRING
danm.k8s.io/clusterNetwork: ## NETWORK_SELECTOR ##
spec:
# DANM recognized Services are selectorless Services, because we want to avoid default Kubernetes controllers to create an Endpoint to a wrong network interface.
# Selectorless Services don't have a spec.selector present in their object.
Expand Down

0 comments on commit 97abcdd

Please sign in to comment.