-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
K8s Network policy Support #1090
base: master
Are you sure you want to change the base?
Conversation
78db510
to
afad54f
Compare
@eng-contiv |
afad54f
to
d94ce81
Compare
6dc0038
to
b518659
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reviewed half of the changes. some suggestions
- logging on success request as well
- logging and avoid infinity loop
- transactional operation should be handled on server side, this way will avoid wired race condition and other concurrency issue
) | ||
|
||
const defaultTenantName = "default" | ||
const defaultNetworkName = "net" | ||
const defaultNetworkName = "default-net" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to provide upgrading guide for this change ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems from earlier conversations, admin would need to create a "default-net" but would have to be a different subnet? Could that break some deployments? Requires network changes to pods and redeploy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return defaultEpgName | ||
} | ||
func getTenantInfo() string { | ||
return defaultTenantName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why these 4 functions are required ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially thinking to use wrapper function to access default tenant and EPG name . look like currently not using it. will remove it
return err | ||
}() != nil { | ||
//XXX:Should we really poll here ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if an infinity loop is here ( without enough logs and hints), no one would able to know how to find out.
please put logs and a maxretry here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idea: log first loop iteration
optionally, additionally we log every minute so we know there is an issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, will put logs with timeout
return err | ||
}() == nil { | ||
}() == nil { //XXX: Same here as above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
if _, err := k8sNet.contivClient.NetworkGet(defaultTenantName, nwName); err != nil { | ||
if _, err := k8sNet.contivClient.NetworkGet( | ||
defaultTenantName, | ||
nwName); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log the network deleted correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. will put log.
if _, err := k8sNet.contivClient.NetworkGet(defaultTenantName, nwName); err == nil { | ||
if _, err := k8sNet.contivClient.NetworkGet( | ||
defaultTenantName, | ||
nwName); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get network info and log it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to follow up, creating a network that is already there could be construed as a problem, would be good to log so that we can track it down when we have time, I know this was just a reformat
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok , Will put the meaningful logs
return err | ||
}() != nil { | ||
}() != nil { //XXX: Same as above |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
npLog.Errorf("failed to update EPG %v: err:%v ", epgName, err) | ||
return err | ||
} | ||
k8sNet.epgName[epgName] = epgName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log default policy and default epg content
//policy := k8sutils.EpgNameToPolicy(epgName, policyName) | ||
if _, err := k8sNet.contivClient. | ||
PolicyGet(tenantName, policyName); err == nil { | ||
npLog.Infof("Policy:%v found contiv", policyName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think instead get-check-create
, the creating checking operations should be handled on server side, this way will avoid concurrency issue
b518659
to
54c9c88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haven't finished reviewing, however, the pod selector logic for pod selectors matchlabels should be AND and not OR:
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is “key”, the operator is “In”, and the values array contains only “value”. The requirements are ANDed.
https://kubernetes.io/docs/api-reference/v1.8/#labelselector-v1-meta
} | ||
type labelPolicySpec struct { | ||
policy []*k8sNetworkPolicy | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this struct is never referenced
Port int | ||
Protocol string | ||
} | ||
type k8sNameSelector struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is namespace selector, can we name the struct k8sNamespaceSelector?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
type k8sIngress struct { | ||
IngressRules []k8sPolicyPorts | ||
IngressPodSelector []*k8sPodSelector | ||
IngressNameSelector *k8sNameSelector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IngressNamespaceSelector?
type npPodInfo struct { | ||
nameSpace string | ||
labelSelectors []string | ||
IP string //??? should care for ipv6 address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use net.IP ?
podEpg string | ||
} | ||
type k8sIPBlockSelector struct { | ||
subNetIps []string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use net.IPNet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how is the "except" clause going to be handled? see in the docs an example:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
//List of Rules Per Policy | ||
// policyRules map[string][]string | ||
//List of Network configured | ||
network map[string]bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pluralize
//List of Network configured | ||
network map[string]bool | ||
//List of EPG configured as set | ||
epgName map[string]bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pluralize
policyPerEpg map[string]map[string][]string | ||
//Cache table for given Pods | ||
//Policy Obj per Policy Name | ||
nwPolicyPerNameSpace map[string][]*k8sNetworkPolicy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nwPoliciesPerNamespace (plural)
return | ||
//Get Network Policy object sets which ToSpec labelMap information match | ||
//with given pods labelMap | ||
func (k8sNet *k8sContext) getMatchToSpecPartNetPolicy( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be easier to unit test if you passed in the podInfo's namespace and labelSelectors values instead of the podInfo itself.
I had a hard time with this function name, could we rename with some hints for input, output, and more specific behavior?
e.g.: getPoliciesForPodByLabels
|
||
if len(toPartPolicy) > 0 { | ||
npLog.Infof("Recv Pod belongs to ToSpec part of Policy") | ||
for _, nw := range toPartPolicy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nw looks like a network, but this is a policy, please update the name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getting closer to first pass complete! but having to rethink how I approach the review since it's so big, working through a top down code execution path review now so I can get some clarity on some implementation choices that have been made.
most of the feedback is minor naming / logging type stuff to help fresh eyes figure out what's going on, but there are a few real concerns sprinkled in
return defaultTenantName | ||
} | ||
|
||
//Start Network Policy feature enabler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you didn't touch, but could you please log infon on first pass through he infinite sleep below?
if _, err := k8sNet.contivClient.NetworkGet(defaultTenantName, nwName); err == nil { | ||
if _, err := k8sNet.contivClient.NetworkGet( | ||
defaultTenantName, | ||
nwName); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to follow up, creating a network that is already there could be construed as a problem, would be good to log so that we can track it down when we have time, I know this was just a reformat
return err | ||
}() != nil { | ||
//XXX:Should we really poll here ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idea: log first loop iteration
optionally, additionally we log every minute so we know there is an issue
if err = k8sNet.createDefaultPolicy( | ||
defaultTenantName, | ||
epgName); err != nil { | ||
npLog.Errorf("failed Default ingress policy EPG %v: err:%v ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
failed creating? maybe could be more clear, default ingress policy for which network/tenant?
return err | ||
} | ||
if err = k8sNet.createEpg(nwName, epgName, policy); err != nil { | ||
npLog.Errorf("failed to update EPG %v: err:%v ", epgName, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
failed to create?
k8sNet.parseIngressPolicy(np.Spec.Ingress, | ||
np.Namespace) | ||
if err != nil { | ||
npLog.Warnf("ignore network policy: %s, %v", np.Name, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like above, maybe not "ignore"
@@ -343,7 +750,7 @@ func (k8sNet *k8sContext) watchK8sEvents(errChan chan error) { | |||
for k8sNet.isLeader() != true { | |||
time.Sleep(time.Millisecond * 100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
marking a spot where we can use a logging retry function
func (k8sNet *k8sContext) processK8sNetworkPolicy( | ||
opCode watch.EventType, np *network_v1.NetworkPolicy) { | ||
if np.Namespace == "kube-system" { //not applicable for system namespace | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor, but since both this and processK8sPod both do this check, you could do it in processK8sEvent instead?
|
||
func (k8sNet *k8sContext) addNetworkPolicy(np *network_v1.NetworkPolicy) { | ||
//check if given Policy already exist | ||
if _, ok := k8sNet.networkPolicy[np.Name]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the top level network policy map as I thought network policy names can be reused across namespaces
} | ||
type k8sNetworkPolicy struct { | ||
PodSelector *k8sPodSelector | ||
Ingress []k8sIngress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the policy name should be here instead of in the pod selector for instance
PodSelector := k8sPodSelector{ | ||
TenantName: getTenantInfo(), | ||
NetworkName: getNetworkInfo(), | ||
GroupName: getEpgInfo()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these three look like placeholders for needed functionality, I think Ranjith's other PR I'm looking at (well I made my own PR based on his) might fit in here for the group name.
Let's discuss/see if we can get to wider consensus that mapping tenant to namespace make sense (thought I saw some conversations that this could be a really good mapping)
for _, l := range label { | ||
if ipMap, ok := | ||
podSelector.labelPodMap[l]; ok { | ||
ipMap[ip] = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be a spot to adjust for the AND logic of the matchLabels
nw *k8sNetworkPolicy, label []string, ip string) { | ||
for _, ingress := range nw.Ingress { | ||
for _, podSelector := range ingress.IngressPodSelector { | ||
npLog.Infof("podSelector:%+v", podSelector) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftover debug statement?
//At each Label Walk all its Ips | ||
for ip := range lMap { | ||
nw.PodSelector.podIps[ip] = ip | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another spot that might be implementing OR logic instead of AND logic for matchLabels
if err = k8sNet.deleteRule(policyName, defaultRuleID); err != nil { | ||
npLog.Errorf("failed to delete default rule, %s", err) | ||
//Consolidate all Ips belongs to Label for Pod Selector object | ||
func (k8sNet *k8sContext) updatePodSelectorPodIps( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function doesn't appear to use k8sContext, be more appropriate to be part of the k8sPodSelector struct?
func (k8sNet *k8sContext) initPodSelectorCacheTbl(m map[string]string, | ||
podSelector *k8sPodSelector) error { | ||
if podSelector == nil { | ||
return fmt.Errorf("Passe Nil Pod Selector reference") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Passed", though this is an internal method and we create the podselector in the caller, why would we expect this to ever be nil?
for key, val := range pod.ObjectMeta.Labels { | ||
lkey := getLabelSelector(key, val) | ||
npLog.Infof("Update label Selector key:%v", lkey) | ||
if ipMap, ok := PodSelector.labelPodMap[lkey]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
marking a spot that I think is aligning with OR based logic for label matching instead of AND
// reset policy to deny on any error | ||
policyResetOnErr := func(tenantName, groupName string) { | ||
if err != nil { | ||
//k8sNet.resetPolicy(tenantName, groupName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing implementation, though this could be touchy, not sure we should back out of the existing working policy . . . we might want to focus more on reconciliation of policy against contiv resources for the next attempt
//Get PolicyMap using EPG | ||
policyMap := k8sNet.policyPerEpg[np.PodSelector.GroupName] | ||
//Check if Policy is already programmed in EPG or not | ||
if _, ok := k8sNet.networkPolicy[np.PodSelector.PolicyName]; !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
policy names I think can be repeated across namespaces, this datastructure would have false positives for existence
if _, ok := k8sNet.networkPolicy[np.PodSelector.PolicyName]; !ok { | ||
//Create Policy and published to ofnet controller | ||
if err = k8sNet.createPolicy( | ||
defaultTenantName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be the podselector's tenant?
} | ||
//k8sNet.policyPerEpg[np.PodSelector.GroupName] = attachPolicy | ||
} else { | ||
//XXX: Need check if policy rules are still same or not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
noting missing implementation
policyName string) (lRules *[]client.Rule) { | ||
var listRules []client.Rule | ||
for _, ingress := range np.Ingress { | ||
isPortsCfg := false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just initiaze the variable to the expression eval instead of separate if block?
attachPolicy := []string{} | ||
for policyN := range policyMap { | ||
attachPolicy = append(attachPolicy, policyN) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
append(attachPolicy, policyMap...) ?
attachPolicy := []string{} | ||
for policyN := range policyMap { | ||
attachPolicy = append(attachPolicy, policyN) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
append(attachPolicy, policyMap...) ? or even
attachPolicy := make([]string, len(policyMap))
copy(attachPolicy, policyMap)
54c9c88
to
531e319
Compare
Include default deny policy Policy cleanup code Pod Add/Delete event processing unit test cases fix fix core issue in pod delete fix core issue in pod delete some cleanup in existing code K8s Network Policy support code changes
After creating EPG, are the spec pods updated to belong to this EPG? If not,will the flow table of ovs install this policy? |
Description of the changes
This is feature commit to support K8s Network Policy at contiv. Using feature, Contiv will support K8s Ingress Network Policy however egress policy support comes in future code commit
Type of fix: New feature
Fixes #1089
Please describe:
= Create k8s Network Policy without configuring Pods
cat network-policy.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-nginx
spec:
podSelector:
matchLabels:
app: nginx
ingress:
matchLabels:
app: myapp
NAME POD-SELECTOR AGE
access-nginx app=nginx 6m
Contiv system after k8s policy
:netctl group ls
Tenant Group Network IP Pool CfgdTag Policies Network profile
default default default-net
default default-group default-net ingress-policy,access-nginx
3. Bringup Ingress policy Pod and Src Pods
:
kubectl create -f nginx-deployment.yaml
:
kubectl create -f apod.yaml
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
apod 1/1 Running 0 2m 10.233.64.8 k8s1
nginx-deployment-431080787-6b6zh 1/1 Running 0 2m 10.233.64.7 k8s1
nginx-deployment-431080787-9d949 1/1 Running 0 2m 10.233.64.6 k8s1
netctl policy rule-ls access-nginx
Incoming Rules:
Rule Priority From EndpointGroup From Network From IpAddress TO IpAddress Protocol Port Action
access-nginx-10.233.64.6-10.233.64.8 2 10.233.64.8 10.233.64.6 0 allow
access-nginx-10.233.64.7-10.233.64.8 2 10.233.64.8 10.233.64.7 0 allow
Outgoing Rules:
Rule Priority To EndpointGroup To Network To IpAddress Protocol Port Action
====
-Result : Make sure update policy rules programmed in contiv system