forked from kubevirt/kubevirt
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
291 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
/* | ||
* 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 2020 Red Hat, Inc. | ||
* | ||
*/ | ||
|
||
package network | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strconv" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
|
||
batchv1 "k8s.io/api/batch/v1" | ||
v12 "k8s.io/api/core/v1" | ||
v13 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/util/intstr" | ||
|
||
v1 "kubevirt.io/client-go/api/v1" | ||
"kubevirt.io/client-go/kubecli" | ||
"kubevirt.io/kubevirt/tests" | ||
cd "kubevirt.io/kubevirt/tests/containerdisk" | ||
) | ||
|
||
var _ = SIGDescribe("Services", func() { | ||
var virtClient kubecli.KubevirtClient | ||
|
||
runTCPClientExpectingHelloWorldFromServer := func(host, port, namespace string) *batchv1.Job { | ||
job := tests.NewHelloWorldJob(host, port) | ||
job, err := virtClient.BatchV1().Jobs(namespace).Create(job) | ||
ExpectWithOffset(1, err).ToNot(HaveOccurred()) | ||
return job | ||
} | ||
|
||
exposeExistingVMISpec := func(vmi *v1.VirtualMachineInstance, subdomain string, hostname string, selectorLabelKey string, selectorLabelValue string) *v1.VirtualMachineInstance { | ||
vmi.Labels = map[string]string{selectorLabelKey: selectorLabelValue} | ||
vmi.Spec.Subdomain = subdomain | ||
vmi.Spec.Hostname = hostname | ||
|
||
return vmi | ||
} | ||
|
||
readyVMI := func(vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance { | ||
_, err := virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Create(vmi) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
return tests.WaitUntilVMIReady(vmi, tests.LoggedInCirrosExpecter) | ||
} | ||
|
||
assertConnectionToExposedService := func(serviceName, namespace string, servicePort int) { | ||
serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace) | ||
|
||
By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN)) | ||
job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace) | ||
|
||
By(fmt.Sprintf("waiting for the job to report a SUCCESSFUL connection attempt to service %s on port %d", serviceFQDN, servicePort)) | ||
tests.WaitForJobToSucceed(&virtClient, job, 90) | ||
} | ||
|
||
assertConnectionToExposedServiceFails := func(serviceName, namespace string, servicePort int) { | ||
serviceFQDN := fmt.Sprintf("%s.%s", serviceName, namespace) | ||
|
||
By(fmt.Sprintf("starting a job which tries to reach the vmi via service %s", serviceFQDN)) | ||
job := runTCPClientExpectingHelloWorldFromServer(serviceFQDN, strconv.Itoa(servicePort), namespace) | ||
|
||
By(fmt.Sprintf("waiting for the job to report a FAILED connection attempt to service %s on port %d", serviceFQDN, servicePort)) | ||
tests.WaitForJobToFail(&virtClient, job, 90) | ||
} | ||
|
||
BeforeEach(func() { | ||
var err error | ||
virtClient, err = kubecli.GetKubevirtClient() | ||
Expect(err).NotTo(HaveOccurred(), "Should successfully initialize an API client") | ||
}) | ||
|
||
Context("Bridge interface binding", func() { | ||
var inboundVMI *v1.VirtualMachineInstance | ||
var serviceName string | ||
|
||
const ( | ||
selectorLabelKey = "expose" | ||
selectorLabelValue = "me" | ||
servicePort = 1500 | ||
) | ||
|
||
createVMISpecWithBridgeInterface := func(vmi *v1.VirtualMachineInstance) *v1.VirtualMachineInstance { | ||
if vmi == nil { | ||
vmi = tests.NewRandomVMIWithEphemeralDiskAndUserdata(cd.ContainerDiskFor(cd.ContainerDiskCirros), "#!/bin/bash\necho 'hello'\n") | ||
} | ||
vmi.Spec.Networks = []v1.Network{*v1.DefaultPodNetwork()} | ||
vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{ | ||
{ | ||
Name: "default", | ||
InterfaceBindingMethod: v1.InterfaceBindingMethod{ | ||
Bridge: &v1.InterfaceBridge{}, | ||
}, | ||
}, | ||
} | ||
|
||
return vmi | ||
} | ||
|
||
createReadyVMIWithBridgeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance { | ||
return readyVMI( | ||
exposeExistingVMISpec( | ||
createVMISpecWithBridgeInterface(nil), subdomain, hostname, selectorLabelKey, selectorLabelValue)) | ||
} | ||
|
||
BeforeEach(func() { | ||
subdomain := "myvmi" | ||
hostname := "my-subdomain" | ||
|
||
inboundVMI = createReadyVMIWithBridgeBindingAndExposedService(hostname, subdomain) | ||
tests.StartTCPServer(inboundVMI, servicePort) | ||
}) | ||
|
||
Context("with a service matching the vmi exposed", func() { | ||
BeforeEach(func() { | ||
serviceName = "myservice" | ||
|
||
service := buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue) | ||
_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
AfterEach(func() { | ||
Expect(virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &v13.DeleteOptions{})).To(Succeed()) | ||
}) | ||
|
||
It("[test_id:1547] should be able to reach the vmi based on labels specified on the vmi", func() { | ||
assertConnectionToExposedService(serviceName, inboundVMI.Namespace, servicePort) | ||
}) | ||
|
||
It("[test_id:1548]should fail to reach the vmi if an invalid servicename is used", func() { | ||
assertConnectionToExposedServiceFails("wrongservice", inboundVMI.Namespace, servicePort) | ||
}) | ||
}) | ||
|
||
Context("with a subdomain and a headless service given", func() { | ||
BeforeEach(func() { | ||
serviceName = inboundVMI.Spec.Subdomain | ||
|
||
service := buildHeadlessServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue) | ||
_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
AfterEach(func() { | ||
Expect(virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &v13.DeleteOptions{})).To(Succeed()) | ||
}) | ||
|
||
It("[test_id:1549]should be able to reach the vmi via its unique fully qualified domain name", func() { | ||
serviceHostnameWithSubdomain := fmt.Sprintf("%s.%s", inboundVMI.Spec.Hostname, inboundVMI.Spec.Subdomain) | ||
assertConnectionToExposedService(serviceHostnameWithSubdomain, inboundVMI.Namespace, servicePort) | ||
}) | ||
}) | ||
}) | ||
|
||
Context("Masquerade interface binding", func() { | ||
var inboundVMI *v1.VirtualMachineInstance | ||
var serviceName string | ||
|
||
const ( | ||
selectorLabelKey = "expose" | ||
selectorLabelValue = "me" | ||
servicePort = 1500 | ||
) | ||
|
||
createReadyVMIWithMasqueradeBindingAndExposedService := func(hostname string, subdomain string) *v1.VirtualMachineInstance { | ||
vmi := tests.NewRandomVMIWithEphemeralDiskAndUserdata(cd.ContainerDiskFor(cd.ContainerDiskCirros), "#!/bin/bash\necho 'hello'\n") | ||
return readyVMI( | ||
exposeExistingVMISpec(vmi, subdomain, hostname, selectorLabelKey, selectorLabelValue)) | ||
} | ||
|
||
BeforeEach(func() { | ||
subdomain := "myvmi" | ||
hostname := "my-subdomain" | ||
|
||
inboundVMI = createReadyVMIWithMasqueradeBindingAndExposedService(hostname, subdomain) | ||
|
||
tests.StartTCPServer(inboundVMI, servicePort) | ||
}) | ||
|
||
Context("with a service matching the vmi exposed", func() { | ||
var isDualStack bool | ||
var ipv6ServiceName string | ||
var ipv6ServicePort int | ||
|
||
BeforeEach(func() { | ||
serviceName = "myservice" | ||
|
||
service := buildServiceSpec(serviceName, servicePort, servicePort, selectorLabelKey, selectorLabelValue) | ||
_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
isDualStack = IsDualStackNetworkConfig() | ||
if isDualStack { | ||
ipv6ServiceName = serviceName + "v6" | ||
ipv6ServicePort = servicePort + 6 | ||
|
||
service := buildIPv6ServiceSpec(ipv6ServiceName, servicePort, ipv6ServicePort, selectorLabelKey, selectorLabelValue) | ||
_, err := virtClient.CoreV1().Services(inboundVMI.Namespace).Create(service) | ||
Expect(err).ToNot(HaveOccurred()) | ||
} | ||
}) | ||
|
||
AfterEach(func() { | ||
deleteServiceResult := virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(serviceName, &v13.DeleteOptions{}) | ||
|
||
if isDualStack { | ||
deleteIPv6ServiceResult := virtClient.CoreV1().Services(inboundVMI.Namespace).Delete(ipv6ServiceName, &v13.DeleteOptions{}) | ||
Expect(deleteIPv6ServiceResult).To(Succeed()) | ||
} | ||
Expect(deleteServiceResult).To(Succeed()) | ||
}) | ||
|
||
It("[test_id:XYZ] should be able to reach the vmi based on labels specified on the vmi", func() { | ||
assertConnectionToExposedService(serviceName, inboundVMI.Namespace, servicePort) | ||
if isDualStack { assertConnectionToExposedService(ipv6ServiceName, inboundVMI.Namespace, ipv6ServicePort) } | ||
}) | ||
|
||
It("[test_id:1548]should fail to reach the vmi if an invalid servicename is used", func() { | ||
assertConnectionToExposedServiceFails("wrongservice", inboundVMI.Namespace, servicePort) | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
||
func buildHeadlessServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *v12.Service { | ||
service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue) | ||
service.Spec.ClusterIP = v12.ClusterIPNone | ||
return service | ||
} | ||
|
||
func buildIPv6ServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *v12.Service { | ||
service := buildServiceSpec(serviceName, exposedPort, portToExpose, selectorKey, selectorValue) | ||
ipv6Family := v12.IPv6Protocol | ||
service.Spec.IPFamily = &ipv6Family | ||
|
||
return service | ||
} | ||
|
||
func buildServiceSpec(serviceName string, exposedPort int, portToExpose int, selectorKey string, selectorValue string) *v12.Service { | ||
return &v12.Service{ | ||
ObjectMeta: v13.ObjectMeta{ | ||
Name: serviceName, | ||
}, | ||
Spec: v12.ServiceSpec{ | ||
Selector: map[string]string{ | ||
selectorKey: selectorValue, | ||
}, | ||
Ports: []v12.ServicePort{ | ||
{Protocol: v12.ProtocolTCP, Port: int32(portToExpose), TargetPort: intstr.FromInt(exposedPort)}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func IsDualStackNetworkConfig() bool { | ||
shouldTestDualStack := os.Getenv("TEST_DUAL_STACK") | ||
isDualStack, err := strconv.ParseBool(shouldTestDualStack) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
return isDualStack | ||
} |
Oops, something went wrong.