Skip to content

Commit

Permalink
virt-api, admitter: Move out slirp binding validation
Browse files Browse the repository at this point in the history
Relocate the slirp admitter validations to the network/admitter package.

Simplify the implementation and provide clear unit tests to the various
checks.

Signed-off-by: Edward Haas <edwardh@redhat.com>
  • Loading branch information
EdDev committed Mar 14, 2024
1 parent 36e9fa8 commit d01e238
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 61 deletions.
10 changes: 9 additions & 1 deletion pkg/network/admitter/BUILD.bazel
Expand Up @@ -2,11 +2,15 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["admit.go"],
srcs = [
"admit.go",
"slirp.go",
],
importpath = "kubevirt.io/kubevirt/pkg/network/admitter",
visibility = ["//visibility:public"],
deps = [
"//pkg/network/vmispec:go_default_library",
"//pkg/virt-config:go_default_library",
"//staging/src/kubevirt.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
Expand All @@ -18,9 +22,13 @@ go_test(
srcs = [
"admit_suite_test.go",
"admit_test.go",
"slirp_test.go",
],
deps = [
":go_default_library",
"//pkg/pointer:go_default_library",
"//pkg/testutils:go_default_library",
"//pkg/virt-config:go_default_library",
"//staging/src/kubevirt.io/api/core/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/api:go_default_library",
"//staging/src/kubevirt.io/client-go/testutils:go_default_library",
Expand Down
58 changes: 58 additions & 0 deletions pkg/network/admitter/slirp.go
@@ -0,0 +1,58 @@
/*
* This file is part of the KubeVirt project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2024 Red Hat, Inc.
*
*/

package admitter

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sfield "k8s.io/apimachinery/pkg/util/validation/field"

"kubevirt.io/kubevirt/pkg/network/vmispec"

virtconfig "kubevirt.io/kubevirt/pkg/virt-config"

v1 "kubevirt.io/api/core/v1"
)

func ValidateSlirpBinding(field *k8sfield.Path, spec *v1.VirtualMachineInstanceSpec, config *virtconfig.ClusterConfig) (causes []metav1.StatusCause) {
for idx, ifaceSpec := range spec.Domain.Devices.Interfaces {
if ifaceSpec.Slirp == nil {
continue
}
net := vmispec.LookupNetworkByName(spec.Networks, ifaceSpec.Name)
if net == nil {
continue
}

if net.Pod == nil {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: "Slirp interface only implemented with pod network",
Field: field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(),
})
} else if !config.IsSlirpInterfaceEnabled() {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: "Slirp interface is not enabled in kubevirt-config",
Field: field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(),
})
}
}
return causes
}
107 changes: 107 additions & 0 deletions pkg/network/admitter/slirp_test.go
@@ -0,0 +1,107 @@
/*
* This file is part of the KubeVirt project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2024 Red Hat, Inc.
*
*/

package admitter_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

k8sfield "k8s.io/apimachinery/pkg/util/validation/field"

v1 "kubevirt.io/api/core/v1"

"kubevirt.io/kubevirt/pkg/pointer"
"kubevirt.io/kubevirt/pkg/testutils"
virtconfig "kubevirt.io/kubevirt/pkg/virt-config"

"kubevirt.io/kubevirt/pkg/network/admitter"
)

var _ = Describe("Validate interface with SLIRP binding", func() {
It("should be rejected if not enabled in the Kubevirt CR", func() {
vmi := v1.VirtualMachineInstance{}
vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Slirp: &v1.InterfaceSlirp{},
},
}}
vmi.Spec.Networks = []v1.Network{{
Name: "default",
NetworkSource: v1.NetworkSource{Pod: &v1.PodNetwork{}},
}}

config, _, _ := testutils.NewFakeClusterConfigUsingKV(&v1.KubeVirt{})

causes := admitter.ValidateSlirpBinding(k8sfield.NewPath("fake"), &vmi.Spec, config)
Expect(causes).To(HaveLen(1))
Expect(causes[0].Message).To(Equal("Slirp interface is not enabled in kubevirt-config"))
})

When("SLIRP is enabled in Kubevirt CR", func() {
var (
kv v1.KubeVirt
config *virtconfig.ClusterConfig
)

BeforeEach(func() {
kv.Spec.Configuration.NetworkConfiguration = &v1.NetworkConfiguration{
PermitSlirpInterface: pointer.P(true),
}
c, _, kvInformer := testutils.NewFakeClusterConfigUsingKV(&kv)
testutils.UpdateFakeKubeVirtClusterConfig(kvInformer, &kv)
config = c
})

It("should be rejected without a pod network", func() {
vmi := v1.VirtualMachineInstance{}
vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Slirp: &v1.InterfaceSlirp{},
},
}}
vmi.Spec.Networks = []v1.Network{{
Name: "default",
NetworkSource: v1.NetworkSource{Multus: &v1.MultusNetwork{}},
}}

causes := admitter.ValidateSlirpBinding(k8sfield.NewPath("fake"), &vmi.Spec, config)
Expect(causes).To(HaveLen(1))
Expect(causes[0].Message).To(Equal("Slirp interface only implemented with pod network"))
})

It("should be accepted with a pod network", func() {
vmi := v1.VirtualMachineInstance{}
vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Slirp: &v1.InterfaceSlirp{},
},
}}
vmi.Spec.Networks = []v1.Network{{
Name: "default",
NetworkSource: v1.NetworkSource{Pod: &v1.PodNetwork{}},
}}

Expect(admitter.ValidateSlirpBinding(k8sfield.NewPath("fake"), &vmi.Spec, config)).To(BeEmpty())
})
})
})
Expand Up @@ -313,10 +313,6 @@ func validateNetworksMatchInterfaces(field *k8sfield.Path, spec *v1.VirtualMachi
func validateInterfaceNetworkBasics(field *k8sfield.Path, networkExists bool, idx int, iface v1.Interface, networkData *v1.Network, config *virtconfig.ClusterConfig, numOfInterfaces int) (causes []metav1.StatusCause) {
if !networkExists {
causes = appendStatusCauseForNetworkNotFound(field, causes, idx, iface)
} else if iface.Slirp != nil && networkData.Pod == nil {
causes = appendStatusCauseForSlirpWithoutPodNetwork(field, causes, idx)
} else if iface.Slirp != nil && networkData.Pod != nil && !config.IsSlirpInterfaceEnabled() {
causes = appendStatusCauseForSlirpNotEnabled(field, causes, idx)
} else if iface.Masquerade != nil && networkData.Pod == nil {
causes = appendStatusCauseForMasqueradeWithoutPodNetwork(field, causes, idx)
} else if iface.Masquerade != nil && link.IsReserved(iface.MacAddress) {
Expand Down Expand Up @@ -573,23 +569,6 @@ func appendStatusCauseForMasqueradeWithoutPodNetwork(field *k8sfield.Path, cause
return causes
}

func appendStatusCauseForSlirpNotEnabled(field *k8sfield.Path, causes []metav1.StatusCause, idx int) []metav1.StatusCause {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: "Slirp interface is not enabled in kubevirt-config",
Field: field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(),
})
return causes
}

func appendStatusCauseForSlirpWithoutPodNetwork(field *k8sfield.Path, causes []metav1.StatusCause, idx int) []metav1.StatusCause {
return append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: "Slirp interface only implemented with pod network",
Field: field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(),
})
}

func appendStatusCauseForNetworkNotFound(field *k8sfield.Path, causes []metav1.StatusCause, idx int, iface v1.Interface) []metav1.StatusCause {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Expand Down
Expand Up @@ -1336,45 +1336,6 @@ var _ = Describe("Validating VMICreate Admitter", func() {
Expect(causes).To(HaveLen(1))
Expect(causes[0].Field).To(Equal("fake.networks[0]"))
})
It("should reject networks with a multus network source and slirp interface", func() {
enableSlirpInterface()
vm := api.NewMinimalVMI("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Slirp: &v1.InterfaceSlirp{},
}}}
vm.Spec.Networks = []v1.Network{
{
Name: "default",
NetworkSource: v1.NetworkSource{
Multus: &v1.MultusNetwork{NetworkName: "default"},
},
},
}

causes := ValidateVirtualMachineInstanceSpec(k8sfield.NewPath("fake"), &vm.Spec, config)
Expect(causes).To(HaveLen(1))
})
It("should accept networks with a pod network source and slirp interface", func() {
enableSlirpInterface()
vm := api.NewMinimalVMI("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Slirp: &v1.InterfaceSlirp{},
}}}

vm.Spec.Networks = []v1.Network{
{
Name: "default",
NetworkSource: v1.NetworkSource{Pod: &v1.PodNetwork{}},
},
}

causes := ValidateVirtualMachineInstanceSpec(k8sfield.NewPath("fake"), &vm.Spec, config)
Expect(causes).To(BeEmpty())
})
It("should reject networks with a passt interface and passt feature gate disabled", func() {
vm := api.NewMinimalVMI("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{{
Expand Down

0 comments on commit d01e238

Please sign in to comment.