-
Notifications
You must be signed in to change notification settings - Fork 333
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
golang based watcher for ovn-kubernetes #110
Conversation
Attn: @shettyg @salv-orlando Big fat PR I know, but its non-intrusive. Please take a look as I look to add callbacks for Network Policy stuff. |
Rajat, |
Signed-off-by: Rajat Chopra <rchopra@redhat.com>
@shettyg just did. realised as soon as I pushed. |
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 just cloned the repo. Before I go deep into the review, should we have a coding style defined? Since I do not know much about golang, do you have a recommendation here? Is golint acceptable? In Python we use flake8, since that takes care of all basic violations, it is easy to just look at important parts of the code.
exit 1 | ||
fi | ||
|
||
install() { |
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 is very RHEL specific. Can we only call this when the platform supports yum?
|
||
### Build | ||
|
||
Ensure go version >= 1.8 |
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.
The hack/build-go.sh seems satisfied with 1.6. I suppose that needs a 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.
There was a reason, but now I cannot recall it.
Will fix if I cannot recollect soon.
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 went through a few golang tutorials. So first set of comments. I am still looking through the pod and endpoint watcher.
var subnet *net.IPNet | ||
|
||
for count > 0 { | ||
if count != 30 { |
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 looks a little suspect. count = 30 initially. So count will never be decremented.
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.
Good catch. Thank you. Fixed.
} | ||
|
||
func calculateMasterSwitchNetwork(clusterNetwork string, hostSubnetLength uint32) (string, error) { | ||
subAllocator, err := netutils.NewSubnetAllocator(clusterNetwork, hostSubnetLength, make([]string, 0)) |
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.
Since the functions being called do not have comments on what they do, can we add a comment here about what they do and what is the return value for both the called functions here?
subrange = append(subrange, hostsubnet) | ||
} | ||
} | ||
masterSwitchNetwork, err := calculateMasterSwitchNetwork(clusterNetwork.String(), hostSubnetLength) |
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.
It is not clear to me how this will work. Above, we look at all the nodes that have already been allocated a subnet. We call a subnet allocator for master. How do we know that they will not overlap?
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.
that is what the line 'subrange = append(subrange, masterSwitchNetwork)' ensures.
The subnetAllocator will give out subnets, but will assume 'subrange' argument as one that is already taken.
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 guess, I understand now. Since master.go will always be run first before any node will get subnet allocated, it takes the first subnet. Right?
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.
Right. Subnet allocation happens only in this code anyway which is only called in the master mode.
It's not intended that we run two copies of this code in parallel, but we could keep locks in place to check for that. Right now that feels more like paranoia than perfection.
go-controller/pkg/cluster/master.go
Outdated
return err | ||
} | ||
subrange = append(subrange, masterSwitchNetwork) | ||
cluster.masterSubnetAllocator, err = netutils.NewSubnetAllocator(clusterNetwork.String(), hostSubnetLength, subrange) |
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.
A comment here about what NewSubnetAllocator does would be nice (as the function itself does not have any documentation).
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.
Yes. Done the needful.
} | ||
} | ||
|
||
cluster.SetupMaster(masterNodeName, masterSwitchNetwork) |
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 "master-init" is called twice, we will call the setup with 2 different subnets?
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.
That is correct. This might break the cluster. So we need to lock it such that only one subnet is alive at one point. Added a TODO 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.
I mean added a TODO at the top of this function
go-controller/pkg/cluster/master.go
Outdated
|
||
cluster.SetupMaster(masterNodeName, masterSwitchNetwork) | ||
|
||
go utilwait.Forever(cluster.watchNodes, 0) |
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.
Can we add a comment here about what the go routine would do. If I understand right, this waits forever watching nodes and calls addNode and deleteNode functions. So a master-init will actually run forever? If so, won't we have 2 watchers in the master node? Would it make sense to add the node watcher functionality when we add other watcher?
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 am done. I am happy with it. I really cannot give any meaningful golang feedback though.
go-controller/pkg/ovn/pods.go
Outdated
if logical_switch != "" { | ||
break | ||
} | ||
if count != 30 { |
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.
The check for count looks suscpect.
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.
Fixed :)
go-controller/pkg/ovn/pods.go
Outdated
p, err := oc.Kube.GetPod(pod.Namespace, pod.Name) | ||
if err != nil { | ||
glog.Errorf("Could not get pod %s/%s for obtaining the logical switch it belongs to", pod.Namespace, pod.Name) | ||
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.
Did you want a continue instead of return? I guess, I don't understand why we have the loop 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.
Fair point. Added continue here.
The loop is primarily there because we are racing with the scheduler. If this controller catches the birth of a pod sooner than the scheduler, we want to give enough tries so that scheduler can assign the NodeName field for us (which decides the logical switch). Will matter in large clusters.
break | ||
} | ||
glog.V(4).Infof("Error while obtaining addresses for %s - %v", portName, err) | ||
time.Sleep(time.Second) |
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 do not see a counter decrement. Though because we use --wait=sb in the ovn-nbctl command, we are supposed to not need the retry logic here. But it does not hurt.
go-controller/pkg/ovn/pods.go
Outdated
err = oc.Kube.SetAnnotationOnPod(pod, "ovn", annotation) | ||
if err != nil { | ||
glog.Errorf("Failed to set annotation on pod %s - %v", pod.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.
We currently are not handling named ports here. The python version does handle it. See "k8s_l4_port_name_cache" in overlay.py. The support necessarily need not come with the first version.
go-controller/pkg/ovn/endpoints.go
Outdated
kapi "k8s.io/client-go/pkg/api/v1" | ||
) | ||
|
||
func (ovn *OvnController) getLoadBalancer(protocol kapi.Protocol) 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.
Since the load-balancer is created before watcher gets any events and it remains constant, a cache here will save ovn-nbctl calls.
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.
Added a TODO here. Will catch it in the next sweep.
// key is of the form "IP:port" (with quotes around) | ||
key := fmt.Sprintf("\"%s:%d\"", serviceIP, port) | ||
|
||
if len(ips) == 0 { |
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.
In the python watcher, we had a watcher for services too. So when a service got deleted, we would get a service delete event and that inturn would cause len(ips) to be zero. When I had tested it, I would not get endpoint events when the service itself got deleted. Has that changed now? Do you get endpoint event with zero ips when a service gets deleted (without pods getting deleted)?
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.
In my tests, the endpoints get deleted when service is deleted. Will keep this in mind though if I see any peculiarities.
On another note, I do not see any code for syncing. In the python version we would do it when the watcher started. Is the syncing taken care by the kubernetes library here? |
…ter decrements in loops
If you mean the re-sync on http disconnect, then yes that is what the golang client library automatically gives us. Also upon a re-sync it gives back any events that we may have missed by listing and comparing with the cache. |
Any opinion on coding style? I am personally not a fan of unlimited line lengths. Should we be using golint? |
@shettyg |
@shettyg I have lint'ed this code(the last commit). Will add the travis part in the next PR as soon as this gets in. |
Code for golang based controller that uses go-client libraries from upstream kubernetes to perform watch/get/post functions for ovn-kubernetes. This code has two main enhancements:
To be done: