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
Binding plugin API #10284
Binding plugin API #10284
Conversation
Skipping CI for Draft Pull Request. |
e8cec21
to
274224f
Compare
e749d60
to
3ab2e63
Compare
3ab2e63
to
14e7fe7
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.
I reviewed all except the net binding plugin: Add binding sidecars to virt-launcher
commit.
I think we also need a feature gate.
Please also specify in the release notes that this is an alphav1 release of this API.
// Image that runs on virt-launcher pod. | ||
//It run services and configures the libvirt domain. |
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.
// SidecarImage references a container image that runs in the virt-launcher pod.
// The sidecar handles (libvirt) domain configuration and optional services.
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.
done.
@@ -1201,6 +1201,9 @@ type Interface struct { | |||
// BindingMethod specifies the method which will be used to connect the interface to the guest. | |||
// Defaults to Bridge. | |||
InterfaceBindingMethod `json:",inline"` | |||
// Binding specifies the binding plugin that will be used to to connect the interface to the guest. |
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.
typo: double "to".
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.
done.
@@ -1310,6 +1313,12 @@ type InterfaceMacvtap struct{} | |||
// InterfacePasst connects to a given network. | |||
type InterfacePasst struct{} | |||
|
|||
// PluggedBinding represents a binding implemented in a plugin. | |||
type PluggedBinding 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.
Perhaps Plugin
instead of Plugged
?
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.
done.
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 about BindingPlugin?
@@ -1310,6 +1313,12 @@ type InterfaceMacvtap struct{} | |||
// InterfacePasst connects to a given network. | |||
type InterfacePasst struct{} | |||
|
|||
// PluggedBinding represents a binding implemented in a plugin. | |||
type PluggedBinding struct { | |||
// Reference to the binding name as denined in the kubevirt CR. |
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.
// Name references to the binding 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.
done.
@@ -58,3 +58,24 @@ func validateInterfaceStateValue(field *k8sfield.Path, spec *v1.VirtualMachineIn | |||
} | |||
return causes | |||
} | |||
|
|||
func validateInterfaceBinding(field *k8sfield.Path, spec *v1.VirtualMachineInstanceSpec) []metav1.StatusCause { |
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.
Someone needs to call it :)
Unit tests will be nice too.
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.
oops:) done.
if iface.InterfaceBindingMethod.Bridge != nil || | ||
iface.InterfaceBindingMethod.Slirp != nil || | ||
iface.InterfaceBindingMethod.Masquerade != nil || | ||
iface.InterfaceBindingMethod.SRIOV != nil || | ||
iface.InterfaceBindingMethod.Macvtap != nil || | ||
iface.InterfaceBindingMethod.Passt != 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.
Can you extract it to a small helper that accepts this InterfaceBindingMethod
struct and checks if one of them exists? It will make this code clearer.
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.
Thanks, you can resolve it now.
@@ -0,0 +1,30 @@ | |||
package services |
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.
Please add the header.
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.
done.
"kubevirt.io/kubevirt/pkg/hooks" | ||
) | ||
|
||
func BindingPluginSidecarList(vmi *v1.VirtualMachineInstance, config *v1.KubeVirtConfiguration) (hooks.HookSidecarList, error) { |
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 need a context for this being network related. Perhaps just add Net
as a prefix.
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.
done.
} | ||
|
||
if !exist { | ||
return nil, fmt.Errorf("couldn't find configuration for bindining: %s", iface.Binding.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.
"binding" >> "network binding".
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.
done.
import ( | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
v1 "kubevirt.io/api/core/v1" |
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: This import is not in the same block as the 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.
done.
@@ -0,0 +1,85 @@ | |||
package services_test |
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.
Please add a header.
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.
done.
@@ -359,6 +359,12 @@ func (t *templateService) renderLaunchManifest(vmi *v1.VirtualMachineInstance, i | |||
return nil, err | |||
} | |||
|
|||
bindingSidecars, err := BindingPluginSidecarList(vmi, t.clusterConfig.GetConfig()) | |||
requestedHookSidecarList = append(requestedHookSidecarList, bindingSidecars...) |
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 should come after the error check.
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!
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.
done.
shouldSucceed = true | ||
) | ||
|
||
BeforeEach(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.
Leftover.
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.
done.
}, | ||
} | ||
sidecars, err := services.BindingPluginSidecarList(vmi, config) | ||
if shouldSucceed { |
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 usually is a hint that two different test tables are a better fit.
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 don't like this pattern because it makes tests a bit harder to follow, I think its worth adding another DescribeTable.
hooks.HookSidecarList{{Image: testSidecarImage1}}, | ||
shouldSucceed), | ||
Entry("VMI has multiple plugin bindings", | ||
libvmi.New(libvmi.WithInterface(v1.Interface{Name: testNetworkName1, Binding: &v1.PluggedBinding{Name: testBindingName1}}), |
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.
Please add another interface with the same plugin binding as an existing one.
We still need to see one sidecare even more than one interface ref 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.
done, thanks for discovering a bug:)
@@ -1201,6 +1201,9 @@ type Interface struct { | |||
// BindingMethod specifies the method which will be used to connect the interface to the guest. | |||
// Defaults to Bridge. | |||
InterfaceBindingMethod `json:",inline"` | |||
// Binding specifies the binding plugin that will be used to to connect the interface to the guest. | |||
// It provides an alternative to InterfaceBindingMethod. | |||
Binding *PluggedBinding `json:"binding,omitempty"` |
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.
Does it make sense/possible to change InterfaceBindingMethod
attribute to be non mandatory, and make the validation webhook to force having binding-method or binding-plugin?
It could make it easier to work with (e.g: check if iface has both binding-plugin and binding-method: if iface.binding != nil && iface.bindingMethod != nil
).
@@ -1310,6 +1313,12 @@ type InterfaceMacvtap struct{} | |||
// InterfacePasst connects to a given network. | |||
type InterfacePasst struct{} | |||
|
|||
// PluggedBinding represents a binding implemented in a plugin. | |||
type PluggedBinding 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.
How about BindingPlugin?
@@ -58,3 +58,24 @@ func validateInterfaceStateValue(field *k8sfield.Path, spec *v1.VirtualMachineIn | |||
} | |||
return causes | |||
} | |||
|
|||
func validateInterfaceBinding(field *k8sfield.Path, spec *v1.VirtualMachineInstanceSpec) []metav1.StatusCause { |
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: How about validateInterfaceBindingPlugin
?
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 will be incorrect. It checks all binding configuration, even the old one.
}, | ||
} | ||
sidecars, err := services.BindingPluginSidecarList(vmi, config) | ||
if shouldSucceed { |
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 don't like this pattern because it makes tests a bit harder to follow, I think its worth adding another DescribeTable.
} | ||
}, | ||
Entry("VMI has binding plugin but config doesn't exist", | ||
libvmi.New(libvmi.WithInterface(v1.Interface{Name: testNetworkName1, Binding: &v1.PluggedBinding{Name: testBindingName1}}), |
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 it was raised before, we should not use e2e tests helpers in unit tests, isn't it? (talking about libvmi)
pluginInfo, exist = config.NetworkConfiguration.Binding[iface.Binding.Name] | ||
} | ||
|
||
if !exist { |
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.
Please consider adding validation for adding binding plugin to Kubevirt CR, at least that the image is not empty.
Running pods with containers who has empty image should fail.
It may also reduce some error checking like this one if its validated at the API level.
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 not recomended, we are not validating the KV CR and we should not add such dependencies in the webhook for inter components dependencies.
@@ -359,6 +359,12 @@ func (t *templateService) renderLaunchManifest(vmi *v1.VirtualMachineInstance, i | |||
return nil, err | |||
} | |||
|
|||
bindingSidecars, err := BindingPluginSidecarList(vmi, t.clusterConfig.GetConfig()) | |||
requestedHookSidecarList = append(requestedHookSidecarList, bindingSidecars...) |
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!
14e7fe7
to
cb7ff78
Compare
cb7ff78
to
aa33891
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.
Very nice, thanks!
pluginInfo, exist = config.NetworkConfiguration.Binding[iface.Binding.Name] | ||
} | ||
|
||
if !exist { |
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 not recomended, we are not validating the KV CR and we should not add such dependencies in the webhook for inter components dependencies.
// SR-IOV devices are not part of the phases | ||
if iface.SRIOV != nil { | ||
// Binding plugin and SR-IOV devices are not part of the phases | ||
if iface.Binding != nil || iface.SRIOV != 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.
This may not work well when we'll need to work with tap devices (or other future exceptions).
But these thoughts are futuristic, for now this seem fine.
FYI @kvaps |
@@ -1201,6 +1201,9 @@ type Interface struct { | |||
// BindingMethod specifies the method which will be used to connect the interface to the guest. | |||
// Defaults to Bridge. | |||
InterfaceBindingMethod `json:",inline"` | |||
// Binding specifies the binding plugin that will be used to connect the interface to the guest. | |||
// It provides an alternative to InterfaceBindingMethod. |
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.
Lets try to do our best and declare all the new fields as v1alphav1
in order to warn about using them.
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.
done.
@@ -1310,6 +1313,12 @@ type InterfaceMacvtap struct{} | |||
// InterfacePasst connects to a given network. | |||
type InterfacePasst struct{} | |||
|
|||
// PluginBinding represents a binding implemented in a plugin. | |||
type PluginBinding struct { | |||
// Name references to the binding name as denined in the kubevirt CR. |
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.
Lets try to do our best and declare all the new fields as v1alphav1
in order to warn about using them.
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.
done.
|
||
type InterfaceBindingPlugin struct { | ||
// SidecarImage references a container image that runs in the virt-launcher pod. | ||
// The sidecar handles (libvirt) domain configuration and optional services. |
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.
Lets try to do our best and declare all the new fields as v1alphav1
in order to warn about using them.
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.
done.
/hold |
0998c93
to
fb1063e
Compare
pkg/virt-config/feature-gates.go
Outdated
@@ -72,6 +72,8 @@ const ( | |||
VMLiveUpdateFeaturesGate = "VMLiveUpdateFeatures" | |||
// When BochsDisplayForEFIGuests is enabled, EFI guests will be started with Bochs display instead of VGA | |||
BochsDisplayForEFIGuests = "BochsDisplayForEFIGuests" | |||
// BindingPlugingsGate enables using a plugin to bind the pod and the VM network | |||
BindingPlugingsGate = "BindingPlugins" |
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 Network
prefix? It is not clear what binding is referred 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.
done.
/hold cancel |
Can you also add the FG info in the release notes and the PR description? |
The kubevirt CR will contain the sidecar image per each binding. example: piVersion: kubevirt.io/v1 kind: KubeVirt metadata: name: kubevirt namespace: kubevirt spec: configuration: network: binding: bridge: sidecarImage: quay.io/kubevirt/network-bridge-binding Signed-off-by: Alona Paz <alkaplan@redhat.com>
The binding will represent the binding plugin. Signed-off-by: Alona Paz <alkaplan@redhat.com>
fb1063e
to
aef0356
Compare
A VMI interface can have only one binding method. In case binding plugin is define, no other binding method can exist on this interface. Signed-off-by: Alona Paz <alkaplan@redhat.com>
Signed-off-by: Alona Paz <alkaplan@redhat.com>
Since those parts are done by the plugin (cni or sidecar) it should be skipped in the core code. Signed-off-by: Alona Paz <alkaplan@redhat.com> Signed-off-by: Or Mergi <ormergi@redhat.com>
PreferredInterfaceMasquerade shouldn't be applied on an interface with binding plugin. Signed-off-by: Alona Paz <alkaplan@redhat.com>
Signed-off-by: Alona Paz <alkaplan@redhat.com>
aef0356
to
ba7bbb9
Compare
2 lgtms, raising to approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: AlonaKaplan 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 |
/retest-required |
What this PR does / why we need it:
This PR introduces network bindings API as plugins external to KubeVirt
Network bindings are registered on the cluster by specifying on the Kubevirt CR their name and configuration (e.g sidecar image).
example:
On the VM/VMI specification, the binding is referenced by name.
example:
The feature is behind "NetworkBindingPlugins" gate.
Which issue(s) this PR fixes (optional, in
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close the issue(s) when PR gets merged):Fixes #
Special notes for your reviewer:
The API is not final (
v1alphav1
) and may be extended as we add more plugins.Release note: