Skip to content

Commit

Permalink
Merge pull request #38 from kubevirt/firt_e2e
Browse files Browse the repository at this point in the history
e2e, Introduce e2e test: resolving a FQDN entry
  • Loading branch information
kubevirt-bot committed Dec 6, 2022
2 parents bb72278 + b1847b3 commit 812cfcf
Show file tree
Hide file tree
Showing 19 changed files with 1,080 additions and 6 deletions.
2 changes: 1 addition & 1 deletion cluster/cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

export KUBEVIRT_PROVIDER=${KUBEVIRT_PROVIDER:-'k8s-1.25'}
export KUBEVIRT_NUM_NODES=2
export KUBEVIRT_NUM_NODES=1
export KUBEVIRTCI_TAG='2211212125-021efaa'

KUBEVIRTCI_REPO='https://github.com/kubevirt/kubevirtci.git'
Expand Down
4 changes: 2 additions & 2 deletions cluster/up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

set -ex pipefail

export DEPLOY_CNAO=${DEPLOY_CNAO:-false}
export DEPLOY_KUBEVIRT=${DEPLOY_KUBEVIRT:-false}
export DEPLOY_CNAO=${DEPLOY_CNAO:-true}
export DEPLOY_KUBEVIRT=${DEPLOY_KUBEVIRT:-true}

function getLatestPatchVersion {
local major_minors=$1
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ go 1.19

require (
github.com/go-logr/logr v1.2.3
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0
github.com/onsi/ginkgo/v2 v2.1.4
github.com/onsi/gomega v1.19.0
k8s.io/api v0.25.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
kubevirt.io/api v0.58.0
sigs.k8s.io/controller-runtime v0.13.0
)
Expand Down Expand Up @@ -72,12 +75,10 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.25.0 // indirect
k8s.io/apiextensions-apiserver v0.25.0 // indirect
k8s.io/component-base v0.25.0 // indirect
k8s.io/klog/v2 v2.70.1 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
kubevirt.io/containerized-data-importer-api v1.50.0 // indirect
kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0 h1:MjRRgZyTGo90G+UrwlDQjU+uG4Z7By65qvQxGoILT/8=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down
9 changes: 8 additions & 1 deletion hack/functest.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#!/bin/bash -e

echo hello world
source ./cluster/cluster.sh

if ! nslookup www.google.com > /dev/null 2>&1; then
echo "nslookup error"
exit 1
fi

KUBECONFIG=${KUBECONFIG:-$(cluster::kubeconfig)} $GO test -test.timeout=1h -test.v ./tests/... -ginkgo.v
File renamed without changes.
43 changes: 43 additions & 0 deletions tests/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package tests

import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

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

networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
)

var clientInstance client.Client

func createClient() (client.Client, error) {
scheme := runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(networkv1.AddToScheme(scheme))
ctrlOptions := ctrl.Options{
Scheme: scheme,
MetricsBindAddress: "0", // disable metrics
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOptions)
if err != nil {
return nil, err
}
return client.New(mgr.GetConfig(), client.Options{Scheme: mgr.GetScheme(), Mapper: mgr.GetRESTMapper()})
}

func getClient() client.Client {
var err error
if clientInstance == nil {
clientInstance, err = createClient()
if err != nil {
panic(err)
}
}
return clientInstance
}
9 changes: 9 additions & 0 deletions tests/name_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tests

import (
"k8s.io/apimachinery/pkg/util/rand"
)

func randName(name string) string {
return name + "-" + rand.String(5)
}
29 changes: 29 additions & 0 deletions tests/test_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2022.
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.
*/

package tests

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Test Suite")
}
91 changes: 91 additions & 0 deletions tests/vm_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package tests

import (
k8sv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

const virtIO = "virtio"

func getBaseVMISpec() *v1.VirtualMachineInstanceSpec {
return &v1.VirtualMachineInstanceSpec{
Domain: v1.DomainSpec{
Resources: v1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceMemory: resource.MustParse("128Mi"),
},
},
},
}
}

func addContainerDisk(spec *v1.VirtualMachineInstanceSpec, image string) *v1.VirtualMachineInstanceSpec {
disk := &v1.Disk{
Name: "containerdisk",
DiskDevice: v1.DiskDevice{
Disk: &v1.DiskTarget{
Bus: virtIO,
},
},
}
spec.Domain.Devices.Disks = append(spec.Domain.Devices.Disks, *disk)
volume := &v1.Volume{
Name: "containerdisk",
VolumeSource: v1.VolumeSource{
ContainerDisk: &v1.ContainerDiskSource{
Image: image,
},
},
}
spec.Volumes = append(spec.Volumes, *volume)
return spec
}

func getBaseVMI(name string) *v1.VirtualMachineInstance {
baseVMISpec := getBaseVMISpec()

return &v1.VirtualMachineInstance{
TypeMeta: metav1.TypeMeta{
APIVersion: v1.GroupVersion.String(),
Kind: "VirtualMachineInstance",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: *baseVMISpec,
}
}

func addNoCloudDiskWitUserData(spec *v1.VirtualMachineInstanceSpec, data string) *v1.VirtualMachineInstanceSpec {
spec.Domain.Devices.Disks = append(spec.Domain.Devices.Disks, v1.Disk{
Name: "cloudinitdisk",
DiskDevice: v1.DiskDevice{
Disk: &v1.DiskTarget{
Bus: virtIO,
},
},
})

spec.Volumes = append(spec.Volumes, v1.Volume{
Name: "cloudinitdisk",
VolumeSource: v1.VolumeSource{
CloudInitNoCloud: &v1.CloudInitNoCloudSource{
UserData: data,
},
},
})
return spec
}

func CreateVmiObject(name string, namespace string, interfaces []v1.Interface, networks []v1.Network) *v1.VirtualMachineInstance {
vmi := getBaseVMI(randName(name))
vmi.Namespace = namespace
addContainerDisk(&vmi.Spec, "quay.io/kubevirt/cirros-container-disk-demo:devel")
addNoCloudDiskWitUserData(&vmi.Spec, "#!/bin/sh\n\necho 'printed from cloud-init userdata'\n")
vmi.Spec.Domain.Devices.Interfaces = interfaces
vmi.Spec.Networks = networks
return vmi
}
129 changes: 129 additions & 0 deletions tests/vm_startup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package tests

import (
"context"
"fmt"
"os/exec"
"time"

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

k8sv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

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

networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
)

const timeout = 3 * time.Minute
const pollingInterval = 5 * time.Second
const testNamespacePrefix = "secondary-test"
const dnsPort = "31111"
const dnsIP = "127.0.0.1" // Forwarded to the node port - https://github.com/kubevirt/kubevirtci/pull/867
const domain = "vm.secondary.io"

var testNamespace string

var _ = Describe("Virtual Machines Startup", func() {
BeforeEach(func() {
By("Creating test namespace")
testNamespace = randName(testNamespacePrefix)
ns := k8sv1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: testNamespace,
},
}
err := getClient().Create(context.Background(), &ns)
Expect(err).ToNot(HaveOccurred(), "couldn't create namespace for the test")
})

AfterEach(func() {
By("Removing the test namespace")
namespace := &k8sv1.Namespace{}
err := getClient().Get(context.Background(), types.NamespacedName{Name: testNamespace}, namespace)
Expect(err).ToNot(HaveOccurred())
err = getClient().Delete(context.Background(), namespace)
Expect(err).ToNot(HaveOccurred())
Eventually(func() error {
return getClient().Get(context.Background(), types.NamespacedName{Name: testNamespace}, namespace)
}, 240*time.Second, 1*time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(errors.IsNotFound, BeTrue())), fmt.Sprintf("should successfully delete namespace '%s'", testNamespace))
})

Context("with one secondary and no default interface", func() {
const interfaceName = "nic1"
var vmiName string
var vmi *v1.VirtualMachineInstance
BeforeEach(func() {
By("Creating a NetworkAttachmentDefinition")
err := createNetworkAttachmentDefinition(testNamespace, "ptp-conf")
Expect(err).ToNot(HaveOccurred(), "Should successfully create the NAD")
})
BeforeEach(func() {
By("Creating a VirtualMachineInstance")
interfaces := []v1.Interface{{Name: interfaceName, InterfaceBindingMethod: v1.InterfaceBindingMethod{Bridge: &v1.InterfaceBridge{}}}}
networks := []v1.Network{
{Name: interfaceName, NetworkSource: v1.NetworkSource{
Multus: &v1.MultusNetwork{NetworkName: "ptp-conf"},
}},
}
vmi := CreateVmiObject("vmi-sec", testNamespace, interfaces, networks)
vmiName = vmi.Name
err := getClient().Create(context.Background(), vmi)
Expect(err).ToNot(HaveOccurred())
})
It("should get the correct IP when nslookup to the secondary interface FQDN", func() {
By("Invoking nslookup on the VMI interface FQDN")
Eventually(func() bool {
vmi = &v1.VirtualMachineInstance{}
err := getClient().Get(context.Background(), types.NamespacedName{Namespace: testNamespace, Name: vmiName}, vmi)
Expect(err).ToNot(HaveOccurred())
return len(vmi.Status.Interfaces) == 1 && vmi.Status.Interfaces[0].IP != ""
}, timeout, pollingInterval).Should(BeTrue(), "failed to get VMI interface IP")
vmiIp := vmi.Status.Interfaces[0].IP

var nslookupOutput []byte
Eventually(func() error {
var nslookupErr error
nslookupOutput, nslookupErr = exec.Command("nslookup", fmt.Sprintf("-port=%s", dnsPort), fmt.Sprintf("%s.%s.%s.%s", interfaceName, vmiName, testNamespace, domain), dnsIP).CombinedOutput()
return nslookupErr
}, time.Minute, pollingInterval).ShouldNot(HaveOccurred(), fmt.Sprintf("nslookup failed. Output - %s", nslookupOutput))

By("Comparing the VirtualMachineInstance IP to the nslookup result")
Expect(nslookupOutput).To(ContainSubstring(fmt.Sprintf("Address: %s\n", vmiIp)), fmt.Sprintf("nsloookup doesn't return the VMI IP address - %s. nslookup output - %s", vmiIp, nslookupOutput))
})
})
})

func createNetworkAttachmentDefinition(namespace, name string) error {
nad := networkv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: networkv1.NetworkAttachmentDefinitionSpec{
Config: `{
"cniVersion": "0.3.1",
"name": "mynet",
"plugins": [
{
"type": "ptp",
"ipam": {
"type": "static",
"addresses": [
{
"address": "10.10.0.5/24",
"gateway": "10.10.0.254"
}
]
}
}
]
}`,
},
}
return getClient().Create(context.Background(), &nad)
}
Loading

0 comments on commit 812cfcf

Please sign in to comment.