Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions webhooks/virtualmachine/validation/virtualmachine_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

netopv1alpha1 "github.com/vmware-tanzu/net-operator-api/api/v1alpha1"
vpcv1alpha1 "github.com/vmware-tanzu/nsx-operator/pkg/apis/vpc/v1alpha1"

vmopv1 "github.com/vmware-tanzu/vm-operator/api/v1alpha5"
"github.com/vmware-tanzu/vm-operator/api/v1alpha5/sysprep"
ncpv1alpha1 "github.com/vmware-tanzu/vm-operator/external/ncp/api/v1alpha1"
"github.com/vmware-tanzu/vm-operator/pkg/builder"
pkgcfg "github.com/vmware-tanzu/vm-operator/pkg/config"
pkgconst "github.com/vmware-tanzu/vm-operator/pkg/constants"
Expand Down Expand Up @@ -780,7 +782,7 @@ func (v validator) validateNetwork(
p := networkPath.Child("interfaces")

for i, interfaceSpec := range networkSpec.Interfaces {
allErrs = append(allErrs, v.validateNetworkInterfaceSpec(p.Index(i), interfaceSpec, vm.Name)...)
allErrs = append(allErrs, v.validateNetworkInterfaceSpec(ctx, p.Index(i), interfaceSpec, vm.Name)...)
allErrs = append(allErrs, v.validateNetworkInterfaceSpecWithBootstrap(ctx, p.Index(i), interfaceSpec, vm)...)
}
}
Expand Down Expand Up @@ -849,20 +851,77 @@ var macAddressSupportNetworkGroups = []string{
vpcv1alpha1.GroupVersion.Group,
}

type networkProviderValidation struct {
group string
kinds []string
}

var networkProviderValidations = map[pkgcfg.NetworkProviderType]networkProviderValidation{
pkgcfg.NetworkProviderTypeNSXT: {
group: ncpv1alpha1.SchemeGroupVersion.Group,
kinds: []string{"VirtualNetwork"},
},
pkgcfg.NetworkProviderTypeVDS: {
group: netopv1alpha1.SchemeGroupVersion.Group,
kinds: []string{"Network"},
},
pkgcfg.NetworkProviderTypeVPC: {
group: vpcv1alpha1.GroupVersion.Group,
kinds: []string{"Subnet", "SubnetSet"},
},
}

func (v validator) validateNetworkInterfaceNetworkRef(
ctx *pkgctx.WebhookRequestContext,
interfacePath *field.Path,
networkGV schema.GroupVersion,
networkKind string) field.ErrorList {

var allErrs field.ErrorList

providerType := pkgcfg.FromContext(ctx).NetworkProviderType
supported, ok := networkProviderValidations[providerType]
if !ok {
// No supported for this provider type (e.g., Named network provider).
return allErrs
}

if networkGV.Group != "" && networkGV.Group != supported.group {
allErrs = append(allErrs, field.NotSupported(
interfacePath.Child("network", "apiVersion"),
networkGV.Group,
[]string{supported.group}))
}

if networkKind != "" && !slices.Contains(supported.kinds, networkKind) {
allErrs = append(allErrs, field.NotSupported(
interfacePath.Child("network", "kind"),
networkKind,
supported.kinds))
}

return allErrs
}

//nolint:gocyclo
func (v validator) validateNetworkInterfaceSpec(
ctx *pkgctx.WebhookRequestContext,
interfacePath *field.Path,
interfaceSpec vmopv1.VirtualMachineNetworkInterfaceSpec,
vmName string) field.ErrorList {

var allErrs field.ErrorList
var networkIfCRName string
var networkAPIVersion string
var networkName string
var (
allErrs field.ErrorList
networkIfCRName string
networkAPIVersion string
networkName string
networkKind string
)

if interfaceSpec.Network != nil {
networkAPIVersion = interfaceSpec.Network.APIVersion
networkName = interfaceSpec.Network.Name
networkKind = interfaceSpec.Network.Kind
}

var networkGV schema.GroupVersion
Expand All @@ -875,6 +934,8 @@ func (v validator) validateNetworkInterfaceSpec(
}
}

allErrs = append(allErrs, v.validateNetworkInterfaceNetworkRef(ctx, interfacePath, networkGV, networkKind)...)

// The networkInterface CR name ("vmName-networkName-interfaceName" or "vmName-interfaceName") needs to be a DNS1123 Label
if networkName != "" {
networkIfCRName = fmt.Sprintf("%s-%s-%s", vmName, networkName, interfaceSpec.Name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2820,6 +2820,218 @@ func unitTestsValidateCreate() {
},
),
)

DescribeTable("network create - validate network provider API group and kind", doTest,

Entry("allow VirtualNetwork kind with NSXT provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeNSXT
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "vmware.com/v1alpha1",
Kind: "VirtualNetwork",
},
Name: "my-network",
},
},
},
}
},
expectAllowed: true,
},
),
Entry("disallow incorrect API group and kind with NSXT provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeNSXT
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "crd.nsx.vmware.com/v1alpha1",
Kind: "VirtualNetwork",
},
Name: "my-network",
},
},
{
Name: "eth1",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "vmware.com/v1alpha1",
Kind: "SubnetSet",
},
Name: "my-network-2",
},
},
},
}
},
validate: doValidateWithMsg(
`spec.network.interfaces[0].network.apiVersion: Unsupported value: "crd.nsx.vmware.com": supported values: "vmware.com"`,
`spec.network.interfaces[1].network.kind: Unsupported value: "SubnetSet": supported values: "VirtualNetwork"`),
},
),

Entry("allow Network kind with VDS provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeVDS
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "netoperator.vmware.com/v1alpha1",
Kind: "Network",
},
Name: "my-network",
},
},
},
}
},
expectAllowed: true,
},
),
Entry("disallow incorrect API group and kind with VDS provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeVDS
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "vmware.com/v1alpha1",
Kind: "Network",
},
Name: "my-network",
},
},
{
Name: "eth1",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "netoperator.vmware.com/v1alpha1",
Kind: "VirtualNetwork",
},
Name: "my-network-2",
},
},
},
}
},
validate: doValidateWithMsg(
`spec.network.interfaces[0].network.apiVersion: Unsupported value: "vmware.com": supported values: "netoperator.vmware.com"`,
`spec.network.interfaces[1].network.kind: Unsupported value: "VirtualNetwork": supported values: "Network"`),
},
),

Entry("allow SubnetSet kind with VPC provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeVPC
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "crd.nsx.vmware.com/v1alpha1",
Kind: "SubnetSet",
},
Name: "my-network",
},
},
},
}
},
expectAllowed: true,
},
),
Entry("allow Subnet kind with VPC provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeVPC
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "crd.nsx.vmware.com/v1alpha1",
Kind: "Subnet",
},
Name: "my-network",
},
},
},
}
},
expectAllowed: true,
},
),

Entry("disallow incorrect API group and kind with VPC provider",
testParams{
setup: func(ctx *unitValidatingWebhookContext) {
pkgcfg.SetContext(ctx, func(config *pkgcfg.Config) {
config.NetworkProviderType = pkgcfg.NetworkProviderTypeVPC
})
ctx.vm.Spec.Network = &vmopv1.VirtualMachineNetworkSpec{
Interfaces: []vmopv1.VirtualMachineNetworkInterfaceSpec{
{
Name: "eth0",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "vmware.com/v1alpha1",
Kind: "SubnetSet",
},
Name: "my-network",
},
},
{
Name: "eth1",
Network: &common.PartialObjectRef{
TypeMeta: metav1.TypeMeta{
APIVersion: "crd.nsx.vmware.com/v1alpha1",
Kind: "VirtualNetwork",
},
Name: "my-network-2",
},
},
},
}
},
validate: doValidateWithMsg(
`spec.network.interfaces[0].network.apiVersion: Unsupported value: "vmware.com": supported values: "crd.nsx.vmware.com"`,
`spec.network.interfaces[1].network.kind: Unsupported value: "VirtualNetwork": supported values: "Subnet", "SubnetSet"`),
},
),
)

DescribeTable("network create - host and domain names", doTest,

Entry("allow simple host name",
Expand Down