Skip to content

Commit

Permalink
e2e test inbound traffic to vmi in istio service-mesh
Browse files Browse the repository at this point in the history
Added test scenarios that cover the followig cases:
1) VMI with explicitly specified ports
 - svc running on a port declared in k8s svc is reachable
 - svc running on a port NOT declared in k8s svc is reachable
2) VMI with default masq interface
 - svc running on a port declared in k8s svc is reachable
 - svc running on a port NOT declared in k8s svc is reachable

Tests require to run with 1.20 and higher provider and
KUBEVIRT_DEPLOY_ISTIO env var must be true so that
Istio service mesh is available in the cluster.

Signed-off-by: Radim Hrazdil <rhrazdil@redhat.com>
  • Loading branch information
Radim Hrazdil committed Mar 23, 2021
1 parent dd85bce commit 83ed698
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 0 deletions.
4 changes: 4 additions & 0 deletions automation/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ elif [[ $TARGET =~ cnao ]]; then
elif [[ $TARGET =~ sig-network ]]; then
export KUBEVIRT_WITH_CNAO=true
export KUBEVIRT_PROVIDER=${TARGET/-sig-network/}
export KUBEVIRT_DEPLOY_ISTIO=true
if [[ $TARGET =~ k8s-1\.1.* ]]; then
export KUBEVIRT_DEPLOY_ISTIO=false
fi
else
export KUBEVIRT_PROVIDER=${TARGET}
fi
Expand Down
3 changes: 3 additions & 0 deletions tests/network/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ go_library(
"networkpolicy.go",
"primary_pod_network.go",
"services.go",
"vmi_istio.go",
"vmi_multus.go",
"vmi_networking.go",
"vmi_slirp_interface.go",
Expand Down Expand Up @@ -41,8 +42,10 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/utils/net:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library",
Expand Down
244 changes: 244 additions & 0 deletions tests/network/vmi_istio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
* 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 2018 Red Hat, Inc.
*
*/

package network

import (
"context"
"encoding/json"
"fmt"
"os"

. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

expect "github.com/google/goexpect"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"

v1 "kubevirt.io/client-go/api/v1"
"kubevirt.io/client-go/kubecli"
"kubevirt.io/kubevirt/tests"
"kubevirt.io/kubevirt/tests/console"
cd "kubevirt.io/kubevirt/tests/containerdisk"
"kubevirt.io/kubevirt/tests/libnet"
)

const (
istioDeployedEnvVariable = "KUBEVIRT_DEPLOY_ISTIO"
istioInjectSidecarAnnotation = "sidecar.istio.io/inject"
istioInjectNamespaceLabel = "istio-injection"
vmiExplicitPortsAppSelector = "istio-vmi-explicit-ports"
vmiDefaultPortsAppSelector = "istio-vmi-default-ports"
svcDeclaredTestPort = 1500
svcUndeclaredTestPort = 1501
)

func istioServiceMeshDeployed() bool {
value := os.Getenv(istioDeployedEnvVariable)
if value == "true" {
return true
}
return false
}

func addInjectIstioProxyAnnotation(vmi *v1.VirtualMachineInstance, value string) {
if vmi.Annotations == nil {
vmi.Annotations = map[string]string{}
}
vmi.Annotations[istioInjectSidecarAnnotation] = value
}

func addAppLabel(vmi *v1.VirtualMachineInstance, value string) {
if vmi.Labels == nil {
vmi.Labels = map[string]string{}
}
vmi.Labels["app"] = value
}

func addLabelToNamespace(namespace, key, value string) {
virtCli, err := kubecli.GetKubevirtClient()
tests.PanicOnError(err)

ns, err := virtCli.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())

old, err := json.Marshal(ns)
Expect(err).ToNot(HaveOccurred())
new := ns.DeepCopy()
if new.Labels == nil {
new.Labels = map[string]string{}
}
new.Labels[key] = value

newJson, err := json.Marshal(new)
Expect(err).ToNot(HaveOccurred())

patch, err := strategicpatch.CreateTwoWayMergePatch(old, newJson, ns)
Expect(err).ToNot(HaveOccurred())

_, err = virtCli.CoreV1().Namespaces().Patch(context.Background(), ns.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
Expect(err).ToNot(HaveOccurred())
}

func newServiceWithAppSelector(name, namespace, selector string) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": selector,
},
Ports: []corev1.ServicePort{
{
Port: svcDeclaredTestPort,
},
},
},
}
}

func newRandomCirrosVMIWithIstioProxyAndEphemeralDiskAndUserDataAndAndServiceAccountDiskAndNetworkAndInterface(serviceAccount, appLabel, istioProxyAnnotationValue, userData string, network *v1.Network, iface *v1.Interface) *v1.VirtualMachineInstance {
vmi := tests.NewRandomVMIWithEphemeralDisk(cd.ContainerDiskFor(cd.ContainerDiskCirros))
tests.AddServiceAccountDisk(vmi, serviceAccount)
tests.AddUserData(vmi, "disk1", userData)
addInjectIstioProxyAnnotation(vmi, istioProxyAnnotationValue)
addAppLabel(vmi, appLabel)
vmi.Spec.Domain.Devices.Interfaces = []v1.Interface{*iface}
vmi.Spec.Networks = []v1.Network{*network}
return vmi
}

var _ = FSIGDescribe("[Serial] Istio", func() {
var err error
var virtClient kubecli.KubevirtClient
var vmiWithExplicitPorts *v1.VirtualMachineInstance
var vmiWithDefaultPorts *v1.VirtualMachineInstance

defaultNetwork := v1.Network{
Name: "default",
NetworkSource: v1.NetworkSource{
Pod: &v1.PodNetwork{},
},
}
defaultInterface := v1.Interface{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Masquerade: &v1.InterfaceMasquerade{},
},
}
interfaceWithExplicitPorts := v1.Interface{
Name: "default",
InterfaceBindingMethod: v1.InterfaceBindingMethod{
Masquerade: &v1.InterfaceMasquerade{},
},
Ports: []v1.Port{
{
Port: svcDeclaredTestPort,
},
{
Port: svcUndeclaredTestPort,
},
},
}

Describe("Virtual machine in a service mesh using masquerade interface with explicit port list", func() {
tests.BeforeAll(func() {
if !istioServiceMeshDeployed() {
Skip("Istio service mesh is required for service-mesh tests to run")
}

tests.BeforeTestCleanup()
virtClient, err = kubecli.GetKubevirtClient()
tests.PanicOnError(err)

vmiWithExplicitPorts = newRandomCirrosVMIWithIstioProxyAndEphemeralDiskAndUserDataAndAndServiceAccountDiskAndNetworkAndInterface(
"default",
vmiExplicitPortsAppSelector,
"true",
"#!/bin/bash\necho 'hello'\n",
&defaultNetwork,
&interfaceWithExplicitPorts,
)
vmiWithDefaultPorts = newRandomCirrosVMIWithIstioProxyAndEphemeralDiskAndUserDataAndAndServiceAccountDiskAndNetworkAndInterface(
"default",
vmiDefaultPortsAppSelector,
"true",
"#!/bin/bash\necho 'hello'\n",
&defaultNetwork,
&defaultInterface,
)
vmiWithExplicitPortsService := newServiceWithAppSelector(fmt.Sprintf("%s-service", vmiWithExplicitPorts.Name), vmiWithExplicitPorts.Namespace, vmiExplicitPortsAppSelector)
vmiWithDefaultPortsService := newServiceWithAppSelector(fmt.Sprintf("%s-service", vmiWithDefaultPorts.Name), vmiWithDefaultPorts.Namespace, vmiDefaultPortsAppSelector)

addLabelToNamespace(vmiWithExplicitPorts.Namespace, istioInjectNamespaceLabel, "enabled")
for _, svc := range []corev1.Service{*vmiWithExplicitPortsService, *vmiWithDefaultPortsService} {
_, err = virtClient.CoreV1().Services(svc.Namespace).Create(context.Background(), &svc, metav1.CreateOptions{})
Expect(err).ShouldNot(HaveOccurred())
}
for _, networkVMI := range []*v1.VirtualMachineInstance{vmiWithExplicitPorts, vmiWithDefaultPorts} {
_, err = virtClient.VirtualMachineInstance(tests.NamespaceTestDefault).Create(networkVMI)
Expect(err).ToNot(HaveOccurred())
}

// Wait for VMIs to become ready
vmiWithExplicitPorts = tests.WaitUntilVMIReady(vmiWithExplicitPorts, libnet.WithIPv6(console.LoginToCirros))
vmiWithDefaultPorts = tests.WaitUntilVMIReady(vmiWithDefaultPorts, libnet.WithIPv6(console.LoginToCirros))

for _, vmi := range []*v1.VirtualMachineInstance{vmiWithExplicitPorts, vmiWithDefaultPorts} {
tests.StartHTTPServer(vmi, svcDeclaredTestPort)
tests.StartHTTPServer(vmi, svcUndeclaredTestPort)
}
})

table.DescribeTable("should be able to reach", func(destination string, port int) {
var srcVMI, dstVMI *v1.VirtualMachineInstance
switch destination {
case "vmiWithExplicitPorts":
srcVMI = vmiWithDefaultPorts
dstVMI = vmiWithExplicitPorts
case "vmiWithDefaultPorts":
srcVMI = vmiWithExplicitPorts
dstVMI = vmiWithDefaultPorts
}
dstVMIAddress := dstVMI.Status.Interfaces[0].IP

By(fmt.Sprintf("checking that service running on port %d can be reached", port))
cmdCheck := fmt.Sprintf("curl %s:%d", dstVMIAddress, port)
err = console.SafeExpectBatch(srcVMI, []expect.Batcher{
&expect.BSnd{S: "\n"},
&expect.BExp{R: console.PromptExpression},
&expect.BSnd{S: cmdCheck},
&expect.BExp{R: console.PromptExpression},
&expect.BSnd{S: "echo $?\n"},
&expect.BExp{R: console.RetValue("0")},
}, 180)
},
table.Entry("declared port in VMI with explicit ports", "vmiWithExplicitPorts", svcDeclaredTestPort),
table.Entry("undeclared port in VMI with explicit ports", "vmiWithExplicitPorts", svcUndeclaredTestPort),
table.Entry("declared port in VMI with default masquerade interface", "vmiWithDefaultPorts", svcUndeclaredTestPort),
table.Entry("undeclared port in VMI with default masquerade interface", "vmiWithDefaultPorts", svcUndeclaredTestPort),
)
})
})

0 comments on commit 83ed698

Please sign in to comment.