Skip to content

Commit

Permalink
adding a validation webhook to verify network api
Browse files Browse the repository at this point in the history
Signed-off-by: Vladik Romanovsky <[email protected]>
  • Loading branch information
vladikr committed Jun 8, 2018
1 parent e1e0797 commit 37f1025
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 0 deletions.
48 changes: 48 additions & 0 deletions pkg/virt-api/validating-webhook/validating-webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
const (
cloudInitMaxLen = 2048
arrayLenMax = 256
maxNetworks = 1
)

func getAdmissionReview(r *http.Request) (*v1beta1.AdmissionReview, error) {
Expand Down Expand Up @@ -302,6 +303,7 @@ func validateVirtualMachineSpec(field *k8sfield.Path, spec *v1.VirtualMachineSpe
var causes []metav1.StatusCause
volumeToDiskIndexMap := make(map[string]int)
volumeNameMap := make(map[string]*v1.Volume)
networkNameMap := make(map[string]*v1.Network)

if len(spec.Domain.Devices.Disks) > arrayLenMax {
causes = append(causes, metav1.StatusCause{
Expand Down Expand Up @@ -352,6 +354,23 @@ func validateVirtualMachineSpec(field *k8sfield.Path, spec *v1.VirtualMachineSpe
})
}

// TODO: Currently, we support only a single network interface attached to a single pod network
if len(spec.Domain.Devices.Interfaces) > maxNetworks {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: fmt.Sprintf("%s list exceeds the %d element limit in length", field.Child("domain", "devices", "interfaces").String(), maxNetworks),
Field: field.Child("domain", "devices", "interfaces").String(),
})
return causes
} else if len(spec.Networks) > maxNetworks {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: fmt.Sprintf("%s list exceeds the %d element limit in length", field.Child("networks").String(), maxNetworks),
Field: field.Child("networks").String(),
})
return causes
}

for _, volume := range spec.Volumes {
volumeNameMap[volume.Name] = &volume
}
Expand Down Expand Up @@ -392,6 +411,35 @@ func validateVirtualMachineSpec(field *k8sfield.Path, spec *v1.VirtualMachineSpe
}
}

if len(spec.Networks) > 0 && len(spec.Domain.Devices.Interfaces) > 0 {
for _, network := range spec.Networks {
networkNameMap[network.Name] = &network
}

// Validate that each interface has a matching network
for idx, iface := range spec.Domain.Devices.Interfaces {

_, networkExists := networkNameMap[iface.Name]

if !networkExists {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: fmt.Sprintf("%s '%s' not found.", field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(), iface.Name),
Field: field.Child("domain", "devices", "interfaces").Index(idx).Child("name").String(),
})
}

// verify that pod network is selected
if spec.Networks[0].Pod == nil {
causes = append(causes, metav1.StatusCause{
Type: metav1.CauseTypeFieldValueInvalid,
Message: fmt.Sprintf("only a %s network source can be selected.", field.Child("domain", "devices", "networks").Index(0).Child("pod").String()),
Field: field.Child("domain", "devices", "networks").Index(0).Child("pod").String(),
})
}
}
}

causes = append(causes, validateDomainSpec(field.Child("domain"), &spec.Domain)...)
causes = append(causes, validateVolumes(field.Child("volumes"), spec.Volumes)...)
return causes
Expand Down
62 changes: 62 additions & 0 deletions pkg/virt-api/validating-webhook/validating-webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,68 @@ var _ = Describe("Validating Webhook", func() {
},
}, 0),
)
It("should accept a single interface and network", func() {
vm := v1.NewMinimalVM("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultNetworkInterface()}
vm.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()}

causes := validateVirtualMachineSpec(k8sfield.NewPath("fake"), &vm.Spec)
Expect(len(causes)).To(Equal(0))
})
It("should reject interface lists with more than one element", func() {
vm := v1.NewMinimalVM("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{
*v1.DefaultNetworkInterface(),
*v1.DefaultNetworkInterface()}

causes := validateVirtualMachineSpec(k8sfield.NewPath("fake"), &vm.Spec)
// if this is processed correctly, it should result in a single error
// If multiple causes occurred, then the spec was processed too far.
Expect(len(causes)).To(Equal(1))
Expect(causes[0].Field).To(Equal("fake.domain.devices.interfaces"))
})
It("should reject network lists with more than one element", func() {
vm := v1.NewMinimalVM("testvm")
vm.Spec.Networks = []v1.Network{
*v1.DefaultPodNetwork(),
*v1.DefaultPodNetwork()}
causes := validateVirtualMachineSpec(k8sfield.NewPath("fake"), &vm.Spec)
// if this is processed correctly, it should result in a single error
// If multiple causes occurred, then the spec was processed too far.
Expect(len(causes)).To(Equal(1))
Expect(causes[0].Field).To(Equal("fake.networks"))
})

It("should reject interfaces with missing network", func() {
vm := v1.NewMinimalVM("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultNetworkInterface()}
vm.Spec.Networks = []v1.Network{
v1.Network{
Name: "redtest",
NetworkSource: v1.NetworkSource{
Pod: &v1.PodNetwork{},
},
},
}

causes := validateVirtualMachineSpec(k8sfield.NewPath("fake"), &vm.Spec)
Expect(len(causes)).To(Equal(1))
Expect(causes[0].Field).To(Equal("fake.domain.devices.interfaces[0].name"))
})
It("should only accept networks with a pod network source ", func() {
vm := v1.NewMinimalVM("testvm")
vm.Spec.Domain.Devices.Interfaces = []v1.Interface{*v1.DefaultNetworkInterface()}
vm.Spec.Networks = []v1.Network{
v1.Network{
Name: "default",
NetworkSource: v1.NetworkSource{},
},
}

causes := validateVirtualMachineSpec(k8sfield.NewPath("fake"), &vm.Spec)
Expect(len(causes)).To(Equal(1))
Expect(causes[0].Field).To(Equal("fake.domain.devices.networks[0].pod"))
})

})
Context("with Volume", func() {
Expand Down

0 comments on commit 37f1025

Please sign in to comment.