Skip to content

Commit

Permalink
Merge 816a94f into f36bf77
Browse files Browse the repository at this point in the history
  • Loading branch information
maiqueb authored Aug 24, 2020
2 parents f36bf77 + 816a94f commit fc9be0a
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 91 deletions.
3 changes: 3 additions & 0 deletions automation/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ if [[ $TARGET =~ windows.* ]]; then
elif [[ $TARGET =~ cnao ]]; then
export KUBEVIRT_WITH_CNAO=true
export KUBEVIRT_PROVIDER=${TARGET/-cnao/}
elif [[ $TARGET == "k8s-1.18" ]]; then
echo "Will test a dual stack network config"
export TEST_DUAL_STACK=true
else
export KUBEVIRT_PROVIDER=${TARGET}
fi
Expand Down
2 changes: 1 addition & 1 deletion tests/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func NewJob(name string, cmd, args []string, retry, ttlAfterFinished int32, time
// which tries to contact the host on the provided port.
// It expects to receive "Hello World!" to succeed.
func NewHelloWorldJob(host string, port string) *batchv1.Job {
check := []string{fmt.Sprintf(`set -x; x="$(head -n 1 < <(nc %s %s -i 3 -w 3))"; echo "$x" ; if [ "$x" = "Hello World!" ]; then echo "succeeded"; exit 0; else echo "failed"; exit 1; fi`, host, port)}
check := []string{fmt.Sprintf(`set -x; ping -c 1 %s; x="$(head -n 1 < <(nc %s %s -i 3 -w 3))"; echo "$x" ; if [ "$x" = "Hello World!" ]; then echo "succeeded"; exit 0; else echo "failed"; exit 1; fi`, host, host, port)}
job := NewJob("netcat", []string{"/bin/bash", "-c"}, check, JobRetry, JobTTL, JobTimeout)
return job
}
Expand Down
4 changes: 4 additions & 0 deletions tests/network/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go_library(
srcs = [
"framework.go",
"primary_pod_network.go",
"services.go",
],
importpath = "kubevirt.io/kubevirt/tests/network",
visibility = ["//visibility:public"],
Expand All @@ -15,7 +16,10 @@ go_library(
"//tests/containerdisk:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
],
)
283 changes: 283 additions & 0 deletions tests/network/services.go
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
}
Loading

0 comments on commit fc9be0a

Please sign in to comment.