-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
passt, sidecar: Look for optional link #12354
Conversation
7638063
to
861dcfa
Compare
861dcfa
to
047220a
Compare
"kubevirt.io/kubevirt/pkg/network/namescheme" | ||
"kubevirt.io/kubevirt/pkg/network/vmispec" | ||
) | ||
|
||
type NetworkHandler interface { |
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 see no reason for it to be exposed.
- It should describe the behavior, so consider a name like
linkFinder
orlinkGetter
. (please adjust the member name as well please, per your choice)
type NetworkConfiguratorOptions struct { | ||
IstioProxyInjectionEnabled bool | ||
UseVirtioTransitional bool | ||
NetworkHandler NetworkHandler |
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.
Ahh this is a bit ugly.
It is not a configuration option, so it looks like an attempt to ride on some existing struct.
The simple option: Pass it to the NewPasstNetworkConfigurator
directly.
The complex option: Use a builder pattern to avoid adding that nil
all over the places that do not need it.
I am good with both options at this stage.
// link do not exist | ||
// FIXME: This will be configurable in the future. | ||
const ( | ||
optionalLinkName = "ovn-udn1" |
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 you add a link that shows from where this is coming from?
Not a must if it is linked in the commit message.
_, err := p.handler.LinkByName(optionalLinkName) | ||
if 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.
nit: Can be inlined (i.e. if _, err := p...; err != nil {
).
_, notFound := err.(vishnetlink.LinkNotFoundError) | ||
if notFound { |
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.
nit: Same
fakeNetlink := fakenetlink.New() | ||
fakeNetlink.LinkAdd(&vishnetlink.Veth{ | ||
LinkAttrs: vishnetlink.LinkAttrs{ | ||
Name: "ovn-udn1", | ||
}, | ||
}) |
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 think we need to use the fake for this simple test.
Creating a stub should do the job:
type netlinkStub struct{}
func (nl netLinkStub) LinkByName(name string) (vishnetlink.Link, error) {
return &vishnetlink.Veth{LinkAttrs: vishnetlink.LinkAttrs{Name: "ovn-udn1"}}
}
We do not really need to mimic any real logic of netlink here.
@@ -65,9 +73,13 @@ func NewPasstNetworkConfigurator(ifaces []vmschema.Interface, networks []vmschem | |||
if iface.Binding == nil || iface.Binding != nil && iface.Binding.Name != PasstPluginName { | |||
return nil, fmt.Errorf("interface %q is not set with Passt network binding plugin", network.Name) | |||
} | |||
if opts.NetworkHandler == nil { | |||
opts.NetworkHandler = &netlink.NetLink{} |
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 I understand correctly, the unit tests that do not use a stub/fake netlink, will in practice try to call netlink
and look for the interface. We do not really want to mix the OS into this, so this is a good reasoning for not making the link getter optional. But it can be solved by making sure the tests always pass in a stub that returns the error.
Could a CNI not just set en ENV var with the DEV name to use?
|
The CNI is executed in the pod network namespace only, it cannot touch the container env variable. |
The SPEC does at least not permit it.
I still wonder if it could be configured - as we know the pod.
DPs can only do this.
|
047220a
to
9c6f579
Compare
One idea as a follow up PR after we unblock the kubevirt binding for ovn-k primary networks is to add a "intefaceName" option at the Kubevirt CR binding option ? then kubevirt can pass it to the sidecard and also to the NetworkSelectionElement virt-launcher annotation that already has a "interface" field:
For the Kubevirt CR it would be something like
|
61d71ba
to
efde4a6
Compare
@qinqon Maybe the binding plugin's downard API can be utilized to avoid passing the interface name (low level detail, depend on env), in a way it"ll mount the network-status annotation so the sidecar can take the interface name form there. The sidecar could realize the interface-name from the device-info. [1] https://github.com/kubevirt/community/blob/main/design-proposals/network-binding-plugin/network-binding-plugin.md#reacting-to-cni-results-multus-networksstatus-annotation |
/assign |
|
||
sourceLinkName, err := p.discoverSourceLinkName() | ||
if err != nil { | ||
return nil, 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.
nit: I would move the link discovery to top of this function, to make if fail faster.
@@ -88,7 +88,7 @@ func (s V1alpha3Server) OnDefineDomain(_ context.Context, params *hooksV1alpha3. | |||
IstioProxyInjectionEnabled: istioProxyInjectionEnabled, | |||
} | |||
|
|||
passtConfigurator, err := domain.NewPasstNetworkConfigurator(vmi.Spec.Domain.Devices.Interfaces, vmi.Spec.Networks, opts) | |||
passtConfigurator, err := domain.NewPasstNetworkConfigurator(vmi.Spec.Domain.Devices.Interfaces, vmi.Spec.Networks, opts, 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.
Shouldn't it have the concrete link discoverer?
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.
On nill it fallback to netlink, so everything is fine.
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.
Oh, I missed that part.
nit: I would pass the concrete link-discoverer here &netlink.NetLink{}
, instead of having NewPasstNetworkConfigurator
account for it.
IMO it would also make the code cleaner and more predictable.
At some systems the passt binding has to use ovn-udn1 as source link for the domain xml. This change add this as an optional link name and do a fallback to eth0 if the optional link is not found. Signed-off-by: Enrique Llorente <ellorent@redhat.com>
efde4a6
to
873c23d
Compare
My point about providing the ifname via CNI is to make it almost
self-contained. I'd not put it into the KubeVirt cr as somebody has to
configure 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.
Overall looks good
I left one suggestion, please consider it.
@@ -88,7 +88,7 @@ func (s V1alpha3Server) OnDefineDomain(_ context.Context, params *hooksV1alpha3. | |||
IstioProxyInjectionEnabled: istioProxyInjectionEnabled, | |||
} | |||
|
|||
passtConfigurator, err := domain.NewPasstNetworkConfigurator(vmi.Spec.Domain.Devices.Interfaces, vmi.Spec.Networks, opts) | |||
passtConfigurator, err := domain.NewPasstNetworkConfigurator(vmi.Spec.Domain.Devices.Interfaces, vmi.Spec.Networks, opts, 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.
Oh, I missed that part.
nit: I would pass the concrete link-discoverer here &netlink.NetLink{}
, instead of having NewPasstNetworkConfigurator
account for it.
IMO it would also make the code cleaner and more predictable.
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.
Thank you.
The inline comments can be treated later if there is no time at the moment, nothing urgent there.
The usage of the hard-coded optional alternative link name is not that nice, but there is a challenge to pass this information through other means at the moment.
- Passing information from the CNI step to the sidecar is not trivial.
- Adding an API field for this is too noisy.
- Using an admission webhook is possible (adding an env variable for the sidecar given the NAD information) and this could be considered for graduation.
/approve
type defaultNetLinkStub struct{} | ||
|
||
func (nl defaultNetLinkStub) LinkByName(name string) (vishnetlink.Link, error) { | ||
return nil, vishnetlink.LinkNotFoundError{} | ||
} | ||
|
||
type netLinkStub struct{} | ||
|
||
func (nl netLinkStub) LinkByName(name string) (vishnetlink.Link, error) { | ||
return &vishnetlink.Veth{LinkAttrs: vishnetlink.LinkAttrs{Name: name}}, 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.
I usually prefer these stubs at the tail of the file. But we can adjust it as a follow up.
@@ -65,10 +72,14 @@ func NewPasstNetworkConfigurator(ifaces []vmschema.Interface, networks []vmschem | |||
if iface.Binding == nil || iface.Binding != nil && iface.Binding.Name != PasstPluginName { | |||
return nil, fmt.Errorf("interface %q is not set with Passt network binding plugin", network.Name) | |||
} | |||
if linkFinder == 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.
I would prefer not to have this asked here, as it opens up the option in the tests to use the concrete unintentionally.
We can fix it later though.
) | ||
|
||
if _, err := p.linkFinder.LinkByName(optionalLinkName); err != nil { | ||
if _, notFound := err.(vishnetlink.LinkNotFoundError); notFound { |
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.
nit: errors.As
is also an alternative.
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's not working since LinkNotFoundError
do not do a proper error implementation, it just wrap an error
https://github.com/vishvananda/netlink/blob/d13535d71ed3e430e26c7538cd6266d026b26f54/link.go#L1412
This is how is tested at their own tests
https://github.com/vishvananda/netlink/blob/main/link_test.go#L2919
@@ -309,5 +324,29 @@ var _ = Describe("pod network configurator", func() { | |||
|
|||
Expect(testMutator.Mutate(mutatedDomSpec)).To(Equal(mutatedDomSpec)) | |||
}) | |||
It("should set domain interface source link to the optional one if exists", func() { |
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.
Newline is missing (splitting between tests).
|
||
Expect(testMutator.Mutate(mutatedDomSpec)).To(Equal(mutatedDomSpec)) | ||
}) | ||
|
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.
redundant newline
vmschema "kubevirt.io/api/core/v1" | ||
|
||
"kubevirt.io/kubevirt/cmd/sidecars/network-passt-binding/domain" | ||
|
||
domainschema "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api" | ||
) | ||
|
||
type defaultNetLinkStub 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.
nit: You do not really need two stub types.
type netLinkStub struct{
link vishnetlink.Link
err error
}
func (nl netLinkStub) LinkByName(name string) (vishnetlink.Link, error) {
return link, err
}
netlinkStubWithNoLink = &netLinkStub{err: vishnetlink.LinkNotFoundError{}}
And for the single place where you need the link, you just fill it up there:
&netLinkStub{link: &vishnetlink.Veth{LinkAttrs: vishnetlink.LinkAttrs{Name: name}}}
But this is not really a must.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: EdDev The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Required labels detected, running phase 2 presubmits: |
Tnaks so much! We will prepare a jira to make kubevirt ovn-kubernetes primary networks aware and address these comments at follow up PR. |
/test pull-kubevirt-e2e-k8s-1.28-sig-compute |
/cherry-pick release-1.3 |
@qinqon: new pull request created: #12409 In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
What this PR does
Before this PR:
Passt uses the source device "eth0" for domain xml
After this PR:
At some enviroments that should be "ovn-udn1" this PR try this first, if it do not exist do a fallback to eth0.
ovn-kubernetes place where the interface is not eth0:
Why we need it and why it was done in this way
The following tradeoffs were made:
Using harcoded value is not flexible, but we plan to make it configurable in the future.
The following alternatives were considered:
Try to find the interface that has the default gw, problem is that at multus you can configure de default network at other interfaces.
Checklist
This checklist is not enforcing, but it's a reminder of items that could be relevant to every PR.
Approvers are expected to review this list.
Release note